* [PATCH 2/5] ARM: dts: qcom: apq8064: Add riva-pil node
From: Bjorn Andersson @ 2016-12-21 11:49 UTC (permalink / raw)
To: Andy Gross, David Brown
Cc: Mark Rutland, devicetree, linux-arm-msm, linux-kernel,
Rob Herring, John Stultz, linux-soc, linux-arm-kernel
In-Reply-To: <20161221114939.19973-1-bjorn.andersson@linaro.org>
Add nodes for the Riva PIL, IRIS RF module, BT and WiFI services exposed
by the Riva firmware and the related memory reserve.
Also provides pinctrl nodes for devices enabling the riva-pil.
Cc: John Stultz <john.stultz@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
arch/arm/boot/dts/qcom-apq8064-pins.dtsi | 18 +++++++++
arch/arm/boot/dts/qcom-apq8064.dtsi | 69 +++++++++++++++++++++++++++++++-
2 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/qcom-apq8064-pins.dtsi b/arch/arm/boot/dts/qcom-apq8064-pins.dtsi
index 6b801e7e57a2..5c023e649882 100644
--- a/arch/arm/boot/dts/qcom-apq8064-pins.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064-pins.dtsi
@@ -284,4 +284,22 @@
bias-disable = <0>;
};
};
+
+ riva_fm_pin_a: riva-fm-active {
+ pins = "gpio14", "gpio15";
+ function = "riva_fm";
+ };
+
+ riva_bt_pin_a: riva-bt-active {
+ pins = "gpio16", "gpio17";
+ function = "riva_bt";
+ };
+
+ riva_wlan_pin_a: riva-wlan-active {
+ pins = "gpio64", "gpio65", "gpio66", "gpio67", "gpio68";
+ function = "riva_wlan";
+
+ drive-strength = <6>;
+ bias-pull-down;
+ };
};
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index 78bf155a52f3..3dc7a7aa3450 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -21,6 +21,11 @@
reg = <0x80000000 0x200000>;
no-map;
};
+
+ wcnss_mem: wcnss@8f000000 {
+ reg = <0x8f000000 0x700000>;
+ no-map;
+ };
};
cpus {
@@ -179,7 +184,7 @@
};
clocks {
- cxo_board {
+ cxo_board: cxo_board {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <19200000>;
@@ -1419,6 +1424,68 @@
};
};
};
+
+ riva: riva-pil@3204000 {
+ compatible = "qcom,riva-pil";
+
+ reg = <0x03200800 0x1000>, <0x03202000 0x2000>, <0x03204000 0x100>;
+ reg-names = "ccu", "dxe", "pmu";
+
+ interrupts-extended = <&intc GIC_SPI 199 IRQ_TYPE_EDGE_RISING>,
+ <&wcnss_smsm 6 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "wdog", "fatal";
+
+ memory-region = <&wcnss_mem>;
+
+ vddcx-supply = <&pm8921_s3>;
+ vddmx-supply = <&pm8921_l24>;
+ vddpx-supply = <&pm8921_s4>;
+
+ status = "disabled";
+
+ iris {
+ compatible = "qcom,wcn3660";
+
+ clocks = <&cxo_board>;
+ clock-names = "xo";
+
+ vddxo-supply = <&pm8921_l4>;
+ vddrfa-supply = <&pm8921_s2>;
+ vddpa-supply = <&pm8921_l10>;
+ vdddig-supply = <&pm8921_lvs2>;
+ };
+
+ smd-edge {
+ interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
+
+ qcom,ipc = <&l2cc 8 25>;
+ qcom,smd-edge = <6>;
+
+ label = "riva";
+
+ wcnss {
+ compatible = "qcom,wcnss";
+ qcom,smd-channels = "WCNSS_CTRL";
+
+ qcom,mmio = <&riva>;
+
+ bt {
+ compatible = "qcom,wcnss-bt";
+ };
+
+ wifi {
+ compatible = "qcom,wcnss-wlan";
+
+ interrupts = <GIC_SPI 203 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 202 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "tx", "rx";
+
+ qcom,smem-states = <&apps_smsm 10>, <&apps_smsm 9>;
+ qcom,smem-state-names = "tx-enable", "tx-rings-empty";
+ };
+ };
+ };
+ };
};
};
#include "qcom-apq8064-pins.dtsi"
--
2.11.0
^ permalink raw reply related
* [PATCH 1/5] ARM: dts: qcom: apq8064: Add missing scm clock
From: Bjorn Andersson @ 2016-12-21 11:49 UTC (permalink / raw)
To: Andy Gross, David Brown
Cc: Rob Herring, Mark Rutland, linux-arm-msm, linux-soc, devicetree,
linux-arm-kernel, linux-kernel, John Stultz
As per the device tree binding the apq8064 scm node requires the core
clock to be specified, so add this.
Cc: John Stultz <john.stultz@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
---
arch/arm/boot/dts/qcom-apq8064.dtsi | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index 268bd470c865..78bf155a52f3 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -303,6 +303,9 @@
firmware {
scm {
compatible = "qcom,scm-apq8064";
+
+ clocks = <&gcc CE3_CORE_CLK>;
+ clock-names = "core";
};
};
--
2.11.0
^ permalink raw reply related
* Re: [PATCH 2/3] NFC: trf7970a: Add device tree option of 1.8 Volt IO voltage
From: Geoff Lansberry @ 2016-12-21 11:47 UTC (permalink / raw)
To: Mark Greer
Cc: linux-wireless, lauro.venancio, aloisio.almeida, sameo, robh+dt,
mark.rutland, netdev, devicetree, linux-kernel, justin
In-Reply-To: <20161221022347.GB5444@animalcreek.com>
Thanks Mark. Should I resubmit patches with the requested edits today, or wait a bit for more comments? What is the desired etiquette?
> On Dec 20, 2016, at 9:23 PM, Mark Greer <mgreer@animalcreek.com> wrote:
>
>> On Tue, Dec 20, 2016 at 11:16:31AM -0500, Geoff Lansberry wrote:
>> From: Geoff Lansberry <geoff@kuvee.com>
>>
>> The TRF7970A has configuration options for supporting hardware designs
>> with 1.8 Volt or 3.3 Volt IO. This commit adds a device tree option,
>> using a fixed regulator binding, for setting the io voltage to match
>> the hardware configuration. If no option is supplied it defaults to
>> 3.3 volt configuration.
>
> Sign-off ?? Same comment for you other patches.
>
> <time passes>
>
> Okay I see you have it at the end of the patch. It should be here.
> 'git commit -s' is your friend.
>
>> ---
>> .../devicetree/bindings/net/nfc/trf7970a.txt | 4 ++--
>> drivers/nfc/trf7970a.c | 28 +++++++++++++++++++++-
>> 2 files changed, 29 insertions(+), 3 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt
>> index e262ac1..b5777d8 100644
>> --- a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt
>> +++ b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt
>> @@ -21,9 +21,9 @@ Optional SoC Specific Properties:
>> - t5t-rmb-extra-byte-quirk: Specify that the trf7970a has the erratum
>> where an extra byte is returned by Read Multiple Block commands issued
>> to Type 5 tags.
>> +- vdd-io-supply: Regulator specifying voltage for vdd-io
>> - clock-frequency: Set to specify that the input frequency to the trf7970a is 13560000Hz or 27120000Hz
>>
>> -
>> Example (for ARM-based BeagleBone with TRF7970A on SPI1):
>>
>> &spi1 {
>> @@ -41,11 +41,11 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1):
>> <&gpio2 5 GPIO_ACTIVE_LOW>;
>> vin-supply = <&ldo3_reg>;
>> vin-voltage-override = <5000000>;
>> + vdd-io-supply = <&ldo2_reg>;
>> autosuspend-delay = <30000>;
>> irq-status-read-quirk;
>> en2-rf-quirk;
>> t5t-rmb-extra-byte-quirk;
>> - vdd_io_1v8;
>
> It was already mentioned but this shouldn't have been added in the
> previous patch so it shouldn't be here now.
>
>> clock-frequency = <27120000>;
>> status = "okay";
>> };
>> diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
>> index 4e051e9..8a88195 100644
>> --- a/drivers/nfc/trf7970a.c
>> +++ b/drivers/nfc/trf7970a.c
>
>> @@ -2062,6 +2068,7 @@ static int trf7970a_probe(struct spi_device *spi)
>> return ret;
>> }
>>
>> +
>
> Please don't add an extra blank line.
>
>> of_property_read_u32(np, "clock-frequency", &clk_freq);
>> if ((clk_freq != TRF7970A_27MHZ_CLOCK_FREQUENCY) ||
>> (clk_freq != TRF7970A_27MHZ_CLOCK_FREQUENCY)) {
>> @@ -2105,6 +2112,25 @@ static int trf7970a_probe(struct spi_device *spi)
>> if (uvolts > 4000000)
>> trf->chip_status_ctrl = TRF7970A_CHIP_STATUS_VRS5_3;
>>
>> + trf->regulator = devm_regulator_get(&spi->dev, "vdd-io");
>> + if (IS_ERR(trf->regulator)) {
>> + ret = PTR_ERR(trf->regulator);
>> + dev_err(trf->dev, "Can't get VDD_IO regulator: %d\n", ret);
>> + goto err_destroy_lock;
>> + }
>> +
>> + ret = regulator_enable(trf->regulator);
>> + if (ret) {
>> + dev_err(trf->dev, "Can't enable VDD_IO: %d\n", ret);
>> + goto err_destroy_lock;
>> + }
>> +
>> +
>
> Please don't add an extra blank line.
>
>> + if (regulator_get_voltage(trf->regulator) == 1800000) {
>> + trf->io_ctrl = TRF7970A_REG_IO_CTRL_IO_LOW;
>> + dev_dbg(trf->dev, "trf7970a config vdd_io to 1.8V\n");
>> + }
>> +
>> trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops,
>> TRF7970A_SUPPORTED_PROTOCOLS,
>> NFC_DIGITAL_DRV_CAPS_IN_CRC |
>> --
>> Signed-off-by: Geoff Lansberry <geoff@kuvee.com>
>
> Your 'Signed-off-by:' goes at the end of the commit description not here.
>
> Overall, I think you did the right thing (unless someone disagrees).
> Just some minor issues.
>
> Mark
> --
^ permalink raw reply
* [PATCH] ARM64: dts: meson-gxbb-odroidc2: fix GbE tx link breakage
From: Jerome Brunet @ 2016-12-21 11:31 UTC (permalink / raw)
To: Kevin Hilman, Carlo Caione, linux-amlogic, devicetree
Cc: Jerome Brunet, linux-arm-kernel, linux-kernel
OdroidC2 GbE link breaks under heavy tx transfer. This happens even if the
MAC does not enable Energy Efficient Ethernet (No Low Power state Idle on
the Tx path). The problem seems to come from the phy Rx path, entering the
LPI state.
Disabling EEE advertisement on the phy prevent this feature to be
negociated with the link partner and solve the issue.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
This patch is based on Linus recent master branch [0]
This patch depends on the series [1] which has been merged in this branch.
0: ba6d973f78eb ("Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net")
1: http://lkml.kernel.org/r/1480326409-25419-1-git-send-email-jbrunet@baylibre.com
Fix integration of eee-broken-modes
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index 238fbeacd330..d8933e9e9a5a 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -147,6 +147,18 @@
status = "okay";
pinctrl-0 = <ð_rgmii_pins>;
pinctrl-names = "default";
+ phy-handle = <ð_phy0>;
+
+ mdio {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth_phy0: ethernet-phy@0 {
+ reg = <0>;
+ eee-broken-1000t;
+ };
+ };
};
&ir {
--
2.7.4
^ permalink raw reply related
* Re: [alsa-devel] [PATCH v3 1/2] ASoC: cs35l35: Add support for Cirrus CS35L35 Amplifier
From: Charles Keepax @ 2016-12-21 10:53 UTC (permalink / raw)
To: Li Xu
Cc: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
devicetree-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
brian.austin-jGc1dHjMKG3QT0dZR+AlfA, tiwai-IBi9RG/b67k,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, lgirdwood-Re5JQEeQqe8AvxtiuMwx3w,
broonie-DgEjT+Ai2ygdnm+yROfE0A,
Paul.Handrigan-jGc1dHjMKG3QT0dZR+AlfA
In-Reply-To: <7566bac5-e4c4-49ff-91b3-bcd578cef21b-XU/xxMRwCJnfk+Ne4bZl5AC/G2K4zDHf@public.gmane.org>
On Tue, Dec 13, 2016 at 10:59:03AM -0600, Li Xu wrote:
> Add driver support for Cirrus Logic CS35L35 boosted
> speaker amplifier
>
> Signed-off-by: Li Xu <li.xu-jGc1dHjMKG3QT0dZR+AlfA@public.gmane.org>
> ---
Mostly just some minor comments.
> +struct classh_cfg {
> + /*
> + * Class H Algorithm Control Variables
> + * You can either have it done
> + * automatically or you can adjust
> + * these variables for tuning
> + *
> + * if you do not enable the internal algorithm
> + * you will get a set of mixer controls for
> + * Class H tuning
> + *
> + * Section 4.3 of the datasheet
> + */
> + /* Internal ClassH Algorithm */
Feels redundant to have this extra comment after the large comment
before it.
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/version.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
Do we need the workqueue header we don't seem to use any
workqueues?
> +static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol, int event)
> +{
> + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
> + struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
> + int ret = 0;
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
> + CS35L35_MCLK_DIS_MASK, 0 << CS35L35_MCLK_DIS_SHIFT);
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
> + CS35L35_DISCHG_FILT_MASK, 0 << CS35L35_DISCHG_FILT_SHIFT);
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
> + CS35L35_PDN_ALL_MASK, 0);
> + break;
Break should be indented for kernel coding style.
> + case SND_SOC_DAPM_POST_PMD:
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
> + CS35L35_PDN_ALL_MASK, 1);
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
> + CS35L35_DISCHG_FILT_MASK, 1 << CS35L35_DISCHG_FILT_SHIFT);
> +
> + ret = wait_for_completion_timeout(&cs35l35->pdn_done,
> + msecs_to_jiffies(100));
> + if (ret == 0) {
> + dev_err(codec->dev, "TIMEOUT PDN_DONE did not complete in 100ms\n");
> + ret = -ETIMEDOUT;
> + }
> +
> + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
> + CS35L35_MCLK_DIS_MASK, 1 << CS35L35_MCLK_DIS_SHIFT);
> + break;
Ditto.
> + default:
> + dev_err(codec->dev, "Invalid event = 0x%x\n", event);
> + ret = -EINVAL;
> + }
> + return ret;
> +}
> +
> +static int cs35l35_main_amp_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol, int event)
> +{
> + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
> + struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
> + unsigned int reg[4];
> + int i;
> +
> + switch (event) {
> + case SND_SOC_DAPM_PRE_PMU:
> + if (cs35l35->pdata.bst_pdn_fet_on)
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
> + CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETON_SHIFT);
> + else
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
> + CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETOFF_SHIFT);
> + regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
> + CS35L35_AMP_MUTE_MASK, 0 << CS35L35_AMP_MUTE_SHIFT);
> + break;
> + case SND_SOC_DAPM_POST_PMU:
> + usleep_range(5000, 5100);
> + /* If PDM mode we must use VP
> + * for Voltage control
> + */
Does this comment need to split across multiple lines?
> +static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
> +{
> + struct snd_soc_codec *codec = codec_dai->codec;
> + struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
> +
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
> + CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
> + cs35l35->slave_mode = false;
> + break;
> + case SND_SOC_DAIFMT_CBS_CFS:
> + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
> + CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
> + cs35l35->slave_mode = true;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + cs35l35->i2s_mode = true;
> + cs35l35->pdm_mode = false;
> + break;
> + case SND_SOC_DAIFMT_PDM:
> + cs35l35->pdm_mode = true;
> + cs35l35->i2s_mode = false;
Feels a bit redundant to have both of these if they are only ever
a logical inversion of each other.
> +static int cs35l35_get_clk_config(int sysclk, int srate)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
> + if (cs35l35_clk_ctl[i].sysclk == sysclk &&
> + cs35l35_clk_ctl[i].srate == srate)
> + return cs35l35_clk_ctl[i].clk_cfg;
> + }
> + return -EINVAL;
> +}
> +
> +static int cs35l35_pcm_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
> + struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
> + int srate = params_rate(params);
> + int ret = 0;
> + u8 sp_sclks;
> + int audin_format;
> + int errata_chk;
> +
> + int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
> +
> + if (clk_ctl < 0) {
> + dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n",
> + cs35l35->sysclk, srate);
> + return -EINVAL;
> + }
It would normally be slightly better to set constraints in
startup based on the SYSCLK rather than returning an error in
hw_params. This allows user-space to negotiate a rate that is
actually supported and do any sample rate conversion required.
> +
> + ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
> + CS35L35_CLK_CTL2_MASK, clk_ctl);
> + if (ret != 0) {
> + dev_err(codec->dev, "Failed to set port config %d\n", ret);
> + return ret;
> + }
> +
> + /* Rev A0 Errata
> + *
> + * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
> + * the Class H algorithm does not enable weak-drive operation for
> + * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
> + *
> + */
> + errata_chk = clk_ctl & CS35L35_SP_RATE_MASK;
> +
> + if (classh->classh_wk_fet_disable == 0x00 &&
> + (errata_chk == 0x01 || errata_chk == 0x03)) {
> + ret = regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
> + 0 << CS35L35_CH_WKFET_DEL_SHIFT);
> + if (ret != 0) {
> + dev_err(codec->dev, "Failed to set fet config %d\n",
> + ret);
> + return ret;
> + }
> + }
> +
> +/*
> + * You can pull more Monitor data from the SDOUT pin than going to SDIN
> + * Just make sure your SCLK is fast enough to fill the frame
> + */
> + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> + switch (params_width(params)) {
> + case 8:
> + audin_format = CS35L35_SDIN_DEPTH_8;
> + break;
> + case 16:
> + audin_format = CS35L35_SDIN_DEPTH_16;
> + break;
> + case 24:
> + audin_format = CS35L35_SDIN_DEPTH_24;
> + break;
> + default:
> + dev_err(codec->dev, "Unsupported Width %d\n",
> + params_width(params));
> + }
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_AUDIN_DEPTH_CTL, CS35L35_AUDIN_DEPTH_MASK,
> + audin_format << CS35L35_AUDIN_DEPTH_SHIFT);
> + if (cs35l35->pdata.stereo) {
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_AUDIN_DEPTH_CTL, CS35L35_ADVIN_DEPTH_MASK,
> + audin_format << CS35L35_ADVIN_DEPTH_SHIFT);
> + }
> + }
> +/* We have to take the SCLK to derive num sclks
> + * to configure the CLOCK_CTL3 register correctly
> + */
> + if ((cs35l35->sclk / srate) % 4) {
> + dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n",
> + cs35l35->sclk, srate);
> + return -EINVAL;
> + }
Again here it might be slightly better to constraints in startup.
> + sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
> +
> + if (cs35l35->i2s_mode) {
> + /* Only certain ratios are supported in I2S Slave Mode */
> + if (cs35l35->slave_mode) {
> + switch (sp_sclks) {
> + case CS35L35_SP_SCLKS_32FS:
> + case CS35L35_SP_SCLKS_48FS:
> + case CS35L35_SP_SCLKS_64FS:
> + break;
> + default:
> + dev_err(codec->dev, "ratio not supported\n");
> + return -EINVAL;
> + };
> + } else {
> + /* Only certain ratios supported in I2S MASTER Mode */
> + switch (sp_sclks) {
> + case CS35L35_SP_SCLKS_32FS:
> + case CS35L35_SP_SCLKS_64FS:
> + break;
> + default:
> + dev_err(codec->dev, "ratio not supported\n");
> + return -EINVAL;
> + };
> + }
> + ret = regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLK_CTL3, CS35L35_SP_SCLKS_MASK,
> + sp_sclks << CS35L35_SP_SCLKS_SHIFT);
> + if (ret != 0) {
> + dev_err(codec->dev, "Failed to set fsclk %d\n", ret);
> + return ret;
> + }
> + }
> + if (cs35l35->pdm_mode) {
> + regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
> + CS35L35_PDM_MODE_MASK, 1 << CS35L35_PDM_MODE_SHIFT);
> + } else {
> + regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
> + CS35L35_PDM_MODE_MASK, 0 << CS35L35_PDM_MODE_SHIFT);
> + }
This if could be combined with the one above since pdm_mode ==
!i2s_mode.
> + return ret;
> +}
> +
> +
> +static const struct snd_soc_dai_ops cs35l35_ops = {
> + .startup = cs35l35_pcm_startup,
> + .set_fmt = cs35l35_set_dai_fmt,
> + .hw_params = cs35l35_pcm_hw_params,
> + .set_sysclk = cs35l35_dai_set_sysclk,
> +};
> +
> +static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
> + .startup = cs35l35_pdm_startup,
> + .set_fmt = cs35l35_set_dai_fmt,
> + .hw_params = cs35l35_pcm_hw_params,
I would be tempted to rename the function to just
cs35l35_hw_params if it is shared between both PCM and PDM.
> + .set_sysclk = cs35l35_dai_set_sysclk,
> +};
> +
> +
> +static int cs35l35_codec_probe(struct snd_soc_codec *codec)
> +{
> + struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
> + struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
> + struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
> + int ret;
> +
> + cs35l35->codec = codec;
> +
> + /* Set Platform Data */
> + if (cs35l35->pdata.bst_vctl)
> + regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
> + CS35L35_BST_CTL_MASK, cs35l35->pdata.bst_vctl);
> +
> + if (cs35l35->pdata.bst_ipk)
> + regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
> + CS35L35_BST_IPK_MASK,
> + cs35l35->pdata.bst_ipk << CS35L35_BST_IPK_SHIFT);
I believe zero is a valid value for this field, but not the
default. Are we happy that the user can never set this value?
> +
> + if (cs35l35->pdata.gain_zc)
> + regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
> + CS35L35_AMP_GAIN_ZC_MASK,
> + cs35l35->pdata.gain_zc << CS35L35_AMP_GAIN_ZC_SHIFT);
> +
> + if (cs35l35->pdata.aud_channel)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_AUDIN_RXLOC_CTL,
> + CS35L35_AUD_IN_LR_MASK,
> + cs35l35->pdata.aud_channel << CS35L35_AUD_IN_LR_SHIFT);
> +
> + if (cs35l35->pdata.stereo) {
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_ADVIN_RXLOC_CTL, CS35L35_ADV_IN_LR_MASK,
> + cs35l35->pdata.adv_channel << CS35L35_ADV_IN_LR_SHIFT);
> + if (cs35l35->pdata.shared_bst)
> + regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
> + CS35L35_CH_STEREO_MASK, 1 << CS35L35_CH_STEREO_SHIFT);
> + ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls,
> + ARRAY_SIZE(cs35l35_adv_controls));
> + if (ret)
> + return ret;
> + }
> +
> + if (cs35l35->pdata.sp_drv_str)
> + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
> + CS35L35_SP_DRV_MASK,
> + cs35l35->pdata.sp_drv_str << CS35L35_SP_DRV_SHIFT);
> +
> + if (classh->classh_algo_enable) {
> + if (classh->classh_bst_override)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_CTL, CS35L35_CH_BST_OVR_MASK,
> + classh->classh_bst_override << CS35L35_CH_BST_OVR_SHIFT);
> + if (classh->classh_bst_max_limit)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_CTL, CS35L35_CH_BST_LIM_MASK,
> + classh->classh_bst_max_limit << CS35L35_CH_BST_LIM_SHIFT);
This is a single bit, but the default bit is 1, so this code can
never change the value of the field.
> + if (classh->classh_mem_depth)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_CTL, CS35L35_CH_MEM_DEPTH_MASK,
> + classh->classh_mem_depth << CS35L35_CH_MEM_DEPTH_SHIFT);
Again zero is a valid value, and not the default.
> + if (classh->classh_headroom)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_HEADRM_CTL, CS35L35_CH_HDRM_CTL_MASK,
> + classh->classh_headroom << CS35L35_CH_HDRM_CTL_SHIFT);
> + if (classh->classh_release_rate)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_RELEASE_RATE, CS35L35_CH_REL_RATE_MASK,
> + classh->classh_release_rate << CS35L35_CH_REL_RATE_SHIFT);
> + if (classh->classh_wk_fet_disable)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DIS_MASK,
> + classh->classh_wk_fet_disable << CS35L35_CH_WKFET_DIS_SHIFT);
> + if (classh->classh_wk_fet_delay)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
> + classh->classh_wk_fet_delay << CS35L35_CH_WKFET_DEL_SHIFT);
Again zero is a valid value, and not the default.
> + if (classh->classh_wk_fet_thld)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_THLD_MASK,
> + classh->classh_wk_fet_thld << CS35L35_CH_WKFET_THLD_SHIFT);
> + if (classh->classh_vpch_auto)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_AUTO_MASK,
> + classh->classh_vpch_auto << CS35L35_CH_VP_AUTO_SHIFT);
Again single bit with a default of 1.
> + if (classh->classh_vpch_rate)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_RATE_MASK,
> + classh->classh_vpch_rate << CS35L35_CH_VP_RATE_SHIFT);
Again zero is a valid value, and not the default.
> + if (classh->classh_vpch_man)
> + regmap_update_bits(cs35l35->regmap,
> + CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_MAN_MASK,
> + classh->classh_vpch_man << CS35L35_CH_VP_MAN_SHIFT);
> + }
> +
<snip>
> +static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
> + const struct i2c_device_id *id)
> +{
> + struct cs35l35_private *cs35l35;
> + struct cs35l35_platform_data *pdata =
> + dev_get_platdata(&i2c_client->dev);
> + int i;
> + int ret;
> + unsigned int devid = 0;
> + unsigned int reg;
> +
> + cs35l35 = devm_kzalloc(&i2c_client->dev,
> + sizeof(struct cs35l35_private),
> + GFP_KERNEL);
> + if (!cs35l35) {
> + dev_err(&i2c_client->dev, "could not allocate codec\n");
> + return -ENOMEM;
> + }
> +
> + i2c_set_clientdata(i2c_client, cs35l35);
> + cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
> + if (IS_ERR(cs35l35->regmap)) {
> + ret = PTR_ERR(cs35l35->regmap);
> + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
> + goto err;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
> + cs35l35->supplies[i].supply = cs35l35_supplies[i];
> + cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
> +
> + ret = devm_regulator_bulk_get(&i2c_client->dev,
> + cs35l35->num_supplies,
> + cs35l35->supplies);
> + if (ret != 0) {
> + dev_err(&i2c_client->dev,
> + "Failed to request core supplies: %d\n",
> + ret);
> + return ret;
> + }
> +
> + if (pdata) {
> + cs35l35->pdata = *pdata;
> + } else {
> + pdata = devm_kzalloc(&i2c_client->dev,
> + sizeof(struct cs35l35_platform_data),
> + GFP_KERNEL);
> + if (!pdata) {
> + dev_err(&i2c_client->dev,
> + "could not allocate pdata\n");
> + return -ENOMEM;
> + }
> + if (i2c_client->dev.of_node) {
> + ret = cs35l35_handle_of_data(i2c_client, pdata);
> + if (ret != 0)
> + return ret;
> +
> + }
> + cs35l35->pdata = *pdata;
> + }
> +
> + ret = regulator_bulk_enable(cs35l35->num_supplies,
> + cs35l35->supplies);
> + if (ret != 0) {
> + dev_err(&i2c_client->dev,
> + "Failed to enable core supplies: %d\n",
> + ret);
> + return ret;
> + }
> +
> + /* returning NULL can be an option if in stereo mode */
> + cs35l35->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
> + "reset", GPIOD_OUT_LOW);
> + if (IS_ERR(cs35l35->reset_gpio))
> + return PTR_ERR(cs35l35->reset_gpio);
This should be a goto err;
> +
> + if (cs35l35->reset_gpio)
> + gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
gpiod_set_value_can_sleep does an internal NULL check on the GPIO
desc I would be tempted to just rely on that one.
> +
> + init_completion(&cs35l35->pdn_done);
> +
> + ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
> + ARRAY_SIZE(cs35l35_errata_patch));
> + if (ret < 0) {
> + dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
> + return ret;
This should be a goto err;
> + }
> +
> + ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
> + cs35l35_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
> + "cs35l35", cs35l35);
> + if (ret != 0) {
> + dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
> + goto err;
> + }
> + /* initialize codec */
> + ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, ®);
> +
> + devid = (reg & 0xFF) << 12;
> + ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, ®);
> + devid |= (reg & 0xFF) << 4;
> + ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, ®);
> + devid |= (reg & 0xF0) >> 4;
> +
> + if (devid != CS35L35_CHIP_ID) {
> + dev_err(&i2c_client->dev,
> + "CS35L35 Device ID (%X). Expected ID %X\n",
> + devid, CS35L35_CHIP_ID);
> + ret = -ENODEV;
> + goto err;
> + }
> +
> + ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, ®);
> + if (ret < 0) {
> + dev_err(&i2c_client->dev, "Get Revision ID failed\n");
> + goto err;
> + }
> +
> + dev_info(&i2c_client->dev,
> + "Cirrus Logic CS35L35 (%x), Revision: %02X\n", devid,
> + ret & 0xFF);
> +
> + /* Set the INT Masks for critical errors */
> + regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1, CS35L35_INT1_CRIT_MASK);
> + regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2, CS35L35_INT2_CRIT_MASK);
> + regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3, CS35L35_INT3_CRIT_MASK);
> + regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4, CS35L35_INT4_CRIT_MASK);
> +
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
> + CS35L35_PWR2_PDN_MASK, CS35L35_PWR2_PDN_MASK);
> +
> + if (cs35l35->pdata.bst_pdn_fet_on)
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
> + CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
> + else
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
> + CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
> +
> + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
> + CS35L35_PWR3_PDN_MASK, CS35L35_PWR3_PDN_MASK);
> +
> + regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
> + CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
> +
> + ret = snd_soc_register_codec(&i2c_client->dev,
> + &soc_codec_dev_cs35l35, cs35l35_dai,
> + ARRAY_SIZE(cs35l35_dai));
> + if (ret < 0) {
> + dev_err(&i2c_client->dev,
> + "%s: Register codec failed\n", __func__);
> + goto err;
> + }
> +
> +err:
> + regulator_bulk_disable(cs35l35->num_supplies,
> + cs35l35->supplies);
> + return ret;
> +}
> +
> +static int cs35l35_i2c_remove(struct i2c_client *client)
> +{
> + snd_soc_unregister_codec(&client->dev);
> + kfree(i2c_get_clientdata(client));
clientdata was allocated with devm this kfree will cause a double
free.
> + return 0;
> +}
Thanks,
Charles
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 3/3] arm64: dts: rockchip: add clk-480m for ehci and ohci of rk3399
From: Xing Zheng @ 2016-12-21 10:44 UTC (permalink / raw)
To: Doug Anderson
Cc: Heiko Stuebner, Frank Wang, Brian Norris, William wu, Rob Herring,
Mark Rutland, Catalin Marinas, Will Deacon, Caesar Wang,
Jianqun Xu, Elaine Zhang,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Dmitry Torokhov, Tao Huang, open list:ARM/Rockchip SoC...
In-Reply-To: <CAD=FV=W1BW6FSZ6MSxR6RhvtZyGsdQbz9vU_QshaQ5A65ENMCg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Heiko, Doug
在 2016年12月17日 01:28, Doug Anderson 写道:
> Hi,
>
> On Thu, Dec 15, 2016 at 10:57 PM, Xing Zheng <zhengxing-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
>> Hi Heiko, Doug,
>>
>> On 2016年12月16日 02:18, Heiko Stuebner wrote:
>>
>> Am Donnerstag, 15. Dezember 2016, 08:34:09 CET schrieb Doug Anderson:
>>
>>
>> I still need to digest all of the things that were added to this
>> thread overnight, but nothing I've seen so far indicates that you need
>> the post-gated clock. AKA I still think you need to redo your patch
>> to replace:
>>
>> clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
>> <&cru SCLK_USBPHY0_480M_SRC>;
>>
>> with:
>>
>> clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
>> <&u2phy0>;
>>
>> Can you please comment on that?
>>
>> Also, with the change, the ehci will keep the clock (and thus the phy)
>> always
>> on. Does the phy-autosuspend even save anything now?
>>
>> In any case, could we make the clock-names entry sound nicer than
>> usbphy0_480m
>> please? bindings/usb/atmel-usb.txt calls its UTMI clock simply "usb_clk",
>> but
>> something like "utmi" should also work.
>> While at it you could also fix up the other clock names to something like
>> "host" and "arbiter" or so?.
>>
>>
>> Heiko
>>
>>
>> The usbphy related clock tress like this:
>>
>>
>> Actually, at drivers/phy/phy-rockchip-inno-usb2.c, we can only
>> enable/disable the master gate via GRF is PHY_PLL, not UTMI_CLK.
>>
>> And the naming style of the "hclk_host0" keep the name "hclk_host0" on the
>> clcok tree diagram:
>>
>>
>> Therefore, could we rename the clock name like this:
>> ----
>> for usb_host0_ehci and usb_host0_ohci:
>> clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
>> <&cru SCLK_U2PHY0>;
>> clock-names = "hclk_host0", "hclk_host0_arb",
>> "sclk_u2phy0";
>>
>> for usb_host1_ehci and usb_host1_ohci:
>> clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
>> <&cru SCLK_U2PHY1>;
>> clock-names = "hclk_host1", "hclk_host1_arb",
>> "sclk_u2phy1";
>> ----
>>
>> BTW, the "arb" is an abbreviation for arbiter.
> You don't specify what this new "SCLK_U2PHY0" ID is, so it's a little
> hard for me to know what you're intending.
>
> ...however, I still don't see any reason why you can't just use the
> solution I proposed. Specifying the clock as "<&u2phy0>" is the
> correct thing to do. The input clock to the EHCI driver is exactly
> the clock provided by the USB PHY with no gate in between (just as I
> said). There is no reason to somehow buffer it by the cru. The cru
> doesn't see this clock and has no reason to be involved.
>
> Note that there were many other comments on this thread besides mine.
> Are you planning to address any of them?
>
> -Doug
>
>
Done, and have resent the patch.
Thanks.
--
- Xing Zheng
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [RESEND PATCH v2] arm64: dts: rockchip: add u2phy clock for ehci and ohci of rk3399
From: Xing Zheng @ 2016-12-21 10:41 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
dianders-F7+t8E8rja9g9hUCZPvPmw, wxt-TNX95d0MmH7DzftRWevZcw,
zhengxing-TNX95d0MmH7DzftRWevZcw,
shawn.lin-TNX95d0MmH7DzftRWevZcw,
briannorris-F7+t8E8rja9g9hUCZPvPmw, jay.xu-TNX95d0MmH7DzftRWevZcw,
zhangqing-TNX95d0MmH7DzftRWevZcw, david.wu-TNX95d0MmH7DzftRWevZcw,
wulf-TNX95d0MmH7DzftRWevZcw, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dianders-hpIqsD4AKlfQT0dZR+AlfA,
frank.wang-TNX95d0MmH7DzftRWevZcw
In-Reply-To: <1482316182-2305-1-git-send-email-zhengxing-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
From: William wu <wulf-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
We found that the suspend process was blocked when it run into
ehci/ohci module due to clk-480m of usb2-phy was disabled.
The root cause is that usb2-phy suspended earlier than ehci/ohci
(usb2-phy will be auto suspended if no devices plug-in). and the
clk-480m provided by it was disabled if no module used. However,
some suspend process related ehci/ohci are base on this clock,
so we should refer it into ehci/ohci driver to prevent this case.
The u2phy clock flow like this:
===
u2phy ________________
| | |-----> UTMI_CLK ---------> | EHCI |
OSC_24M ---|---> PHY_PLL----|----|
|________^_______| |-----> 480M_CLK ---|G|---> | USBPHY_480M_SRC| ----> USBPHY_480M for SoC
|
|
GRF
===
Signed-off-by: William wu <wulf-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Xing Zheng <zhengxing-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
Changes in v2:
- update the commit message
- remove patches whic add and export the USBPHYx_480M_SRC clock IDs
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index b65c193..2ad9255 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -315,8 +315,10 @@
compatible = "generic-ehci";
reg = <0x0 0xfe380000 0x0 0x20000>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
- clock-names = "hclk_host0", "hclk_host0_arb";
+ clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
+ <&u2phy0>;
+ clock-names = "usbhost", "arbiter",
+ "utmi";
phys = <&u2phy0_host>;
phy-names = "usb";
status = "disabled";
@@ -326,8 +328,12 @@
compatible = "generic-ohci";
reg = <0x0 0xfe3a0000 0x0 0x20000>;
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
- clock-names = "hclk_host0", "hclk_host0_arb";
+ clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
+ <&u2phy0>;
+ clock-names = "usbhost", "arbiter",
+ "utmi";
+ phys = <&u2phy0_host>;
+ phy-names = "usb";
status = "disabled";
};
@@ -335,8 +341,10 @@
compatible = "generic-ehci";
reg = <0x0 0xfe3c0000 0x0 0x20000>;
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
- clock-names = "hclk_host1", "hclk_host1_arb";
+ clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
+ <&u2phy1>;
+ clock-names = "usbhost", "arbiter",
+ "utmi";
phys = <&u2phy1_host>;
phy-names = "usb";
status = "disabled";
@@ -346,8 +354,12 @@
compatible = "generic-ohci";
reg = <0x0 0xfe3e0000 0x0 0x20000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
- clock-names = "hclk_host1", "hclk_host1_arb";
+ clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
+ <&u2phy1>;
+ clock-names = "usbhost", "arbiter",
+ "utmi";
+ phys = <&u2phy1_host>;
+ phy-names = "usb";
status = "disabled";
};
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2] arm64: dts: rockchip: add u2phy clock for ehci and ohci of rk3399
From: Xing Zheng @ 2016-12-21 10:29 UTC (permalink / raw)
To: linux-rockchip
Cc: dianders, heiko, William wu, Xing Zheng, Rob Herring,
Mark Rutland, Catalin Marinas, Will Deacon, Douglas Anderson,
Caesar Wang, Brian Norris, Shawn Lin, Jianqun Xu, Elaine Zhang,
David Wu, devicetree, linux-arm-kernel, linux-kernel
From: William wu <wulf@rock-chips.com>
We found that the suspend process was blocked when it run into
ehci/ohci module due to clk-480m of usb2-phy was disabled.
The root cause is that usb2-phy suspended earlier than ehci/ohci
(usb2-phy will be auto suspended if no devices plug-in). and the
clk-480m provided by it was disabled if no module used. However,
some suspend process related ehci/ohci are base on this clock,
so we should refer it into ehci/ohci driver to prevent this case.
The u2phy clock flow like this:
---
Changes in v2:
- update the commit message
- remove patches whic add and export the USBPHYx_480M_SRC clock IDs
u2phy ________________
| | |-----> UTMI_CLK ---------> | EHCI |
OSC_24M ---|---> PHY_PLL----|----|
|________^_______| |-----> 480M_CLK ---|G|---> | USBPHY_480M_SRC| ----> USBPHY_480M for SoC
|
|
GRF
---
Signed-off-by: William wu <wulf@rock-chips.com>
Signed-off-by: Xing Zheng <zhengxing@rock-chips.com>
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index b65c193..2ad9255 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -315,8 +315,10 @@
compatible = "generic-ehci";
reg = <0x0 0xfe380000 0x0 0x20000>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
- clock-names = "hclk_host0", "hclk_host0_arb";
+ clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
+ <&u2phy0>;
+ clock-names = "usbhost", "arbiter",
+ "utmi";
phys = <&u2phy0_host>;
phy-names = "usb";
status = "disabled";
@@ -326,8 +328,12 @@
compatible = "generic-ohci";
reg = <0x0 0xfe3a0000 0x0 0x20000>;
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
- clock-names = "hclk_host0", "hclk_host0_arb";
+ clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
+ <&u2phy0>;
+ clock-names = "usbhost", "arbiter",
+ "utmi";
+ phys = <&u2phy0_host>;
+ phy-names = "usb";
status = "disabled";
};
@@ -335,8 +341,10 @@
compatible = "generic-ehci";
reg = <0x0 0xfe3c0000 0x0 0x20000>;
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
- clock-names = "hclk_host1", "hclk_host1_arb";
+ clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
+ <&u2phy1>;
+ clock-names = "usbhost", "arbiter",
+ "utmi";
phys = <&u2phy1_host>;
phy-names = "usb";
status = "disabled";
@@ -346,8 +354,12 @@
compatible = "generic-ohci";
reg = <0x0 0xfe3e0000 0x0 0x20000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
- clock-names = "hclk_host1", "hclk_host1_arb";
+ clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
+ <&u2phy1>;
+ clock-names = "usbhost", "arbiter",
+ "utmi";
+ phys = <&u2phy1_host>;
+ phy-names = "usb";
status = "disabled";
};
--
2.7.4
^ permalink raw reply related
* [PATCHv2 3/3] arm64: dts: marvell: adjust name of sd-mmc-gop clock in syscon
From: Thomas Petazzoni @ 2016-12-21 10:26 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, linux-clk-u79uwXL29TY76Z2rM5mHXA,
Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala
Cc: Nadav Haklai, Hanna Hawa, Yehuda Yitschak,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni
In-Reply-To: <1482316017-22154-1-git-send-email-thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
This commit adjusts the names of gatable clock #18 of the Marvell Armada
CP110 system controller. This clock not only controls SD/MMC, but also
the GOP (Group Of Ports) used for networking. So the clock is renamed to
{cpm,cps}-sd-mmc-gop instead of {cpm,cps}-sd-mmc.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi | 2 +-
arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
index 602e2c2..895babc 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
@@ -74,7 +74,7 @@
"cpm-gop-dp", "none", "cpm-pcie_x10",
"cpm-pcie_x11", "cpm-pcie_x4", "cpm-pcie-xor",
"cpm-sata", "cpm-sata-usb", "cpm-main",
- "cpm-sd-mmc", "none", "none",
+ "cpm-sd-mmc-gop", "none", "none",
"cpm-slow-io", "cpm-usb3h0", "cpm-usb3h1",
"cpm-usb3dev", "cpm-eip150", "cpm-eip197";
};
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
index 6bf9e24..db99646 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
@@ -74,7 +74,7 @@
"cps-gop-dp", "none", "cps-pcie_x10",
"cps-pcie_x11", "cps-pcie_x4", "cps-pcie-xor",
"cps-sata", "cps-sata-usb", "cps-main",
- "cps-sd-mmc", "none", "none",
+ "cps-sd-mmc-gop", "none", "none",
"cps-slow-io", "cps-usb3h0", "cps-usb3h1",
"cps-usb3dev", "cps-eip150", "cps-eip197";
};
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv2 2/3] clk: mvebu: adjust clock handling for the CP110 system controller
From: Thomas Petazzoni @ 2016-12-21 10:26 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, linux-clk, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, devicetree,
Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala
Cc: Nadav Haklai, Hanna Hawa, Yehuda Yitschak, linux-arm-kernel,
Thomas Petazzoni
In-Reply-To: <1482316017-22154-1-git-send-email-thomas.petazzoni@free-electrons.com>
This commit:
- makes the GOP_DP (bit 9) gatable clock a child clock of the
SD_MMC_GOP (bit 18) clock, as it should have been. The clock for bit
18 was just named SD_MMC, but since it also covers the GOP block, it
is renamed SD_MMC_GOP.
- makes the MG (bit 5) gatable clock a child clock of the MG_CORE
clock (bit 6)
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
drivers/clk/mvebu/cp110-system-controller.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c
index f2303da..8038b93 100644
--- a/drivers/clk/mvebu/cp110-system-controller.c
+++ b/drivers/clk/mvebu/cp110-system-controller.c
@@ -64,8 +64,11 @@ enum {
#define CP110_GATE_NAND 2
#define CP110_GATE_PPV2 3
#define CP110_GATE_SDIO 4
+#define CP110_GATE_MG 5
+#define CP110_GATE_MG_CORE 6
#define CP110_GATE_XOR1 7
#define CP110_GATE_XOR0 8
+#define CP110_GATE_GOP_DP 9
#define CP110_GATE_PCIE_X1_0 11
#define CP110_GATE_PCIE_X1_1 12
#define CP110_GATE_PCIE_X4 13
@@ -73,7 +76,7 @@ enum {
#define CP110_GATE_SATA 15
#define CP110_GATE_SATA_USB 16
#define CP110_GATE_MAIN 17
-#define CP110_GATE_SDMMC 18
+#define CP110_GATE_SDMMC_GOP 18
#define CP110_GATE_SLOW_IO 21
#define CP110_GATE_USB3H0 22
#define CP110_GATE_USB3H1 23
@@ -302,6 +305,11 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
"gate-clock-output-names",
CP110_GATE_MAIN, &parent);
break;
+ case CP110_GATE_MG:
+ of_property_read_string_index(np,
+ "gate-clock-output-names",
+ CP110_GATE_MG_CORE, &parent);
+ break;
case CP110_GATE_NAND:
parent = nand_name;
break;
@@ -309,9 +317,10 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
parent = ppv2_name;
break;
case CP110_GATE_SDIO:
+ case CP110_GATE_GOP_DP:
of_property_read_string_index(np,
"gate-clock-output-names",
- CP110_GATE_SDMMC, &parent);
+ CP110_GATE_SDMMC_GOP, &parent);
break;
case CP110_GATE_XOR1:
case CP110_GATE_XOR0:
--
2.7.4
^ permalink raw reply related
* [PATCHv2 1/3] dt-bindings: arm: update Armada CP110 system controller binding
From: Thomas Petazzoni @ 2016-12-21 10:26 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, linux-clk-u79uwXL29TY76Z2rM5mHXA,
Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory Clement,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Ian Campbell,
Pawel Moll, Mark Rutland, Kumar Gala
Cc: Nadav Haklai, Hanna Hawa, Yehuda Yitschak,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni
In-Reply-To: <1482316017-22154-1-git-send-email-thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
It turns out that in the CP110 HW block present in Marvell Armada
7K/8K SoCs, gatable clock n°18 not only controls SD/MMC, but also the
GOP block. This commit updates the Device Tree binding for this piece
of hardware accordingly.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
.../devicetree/bindings/arm/marvell/cp110-system-controller0.txt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller0.txt b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller0.txt
index 30c5469..07dbb35 100644
--- a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller0.txt
+++ b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller0.txt
@@ -45,7 +45,7 @@ The following clocks are available:
- 1 15 SATA
- 1 16 SATA USB
- 1 17 Main
- - 1 18 SD/MMC
+ - 1 18 SD/MMC/GOP
- 1 21 Slow IO (SPI, NOR, BootROM, I2C, UART)
- 1 22 USB3H0
- 1 23 USB3H1
@@ -65,7 +65,7 @@ Required properties:
"cpm-audio", "cpm-communit", "cpm-nand", "cpm-ppv2", "cpm-sdio",
"cpm-mg-domain", "cpm-mg-core", "cpm-xor1", "cpm-xor0", "cpm-gop-dp", "none",
"cpm-pcie_x10", "cpm-pcie_x11", "cpm-pcie_x4", "cpm-pcie-xor", "cpm-sata",
- "cpm-sata-usb", "cpm-main", "cpm-sd-mmc", "none", "none", "cpm-slow-io",
+ "cpm-sata-usb", "cpm-main", "cpm-sd-mmc-gop", "none", "none", "cpm-slow-io",
"cpm-usb3h0", "cpm-usb3h1", "cpm-usb3dev", "cpm-eip150", "cpm-eip197";
Example:
@@ -78,6 +78,6 @@ Example:
gate-clock-output-names = "cpm-audio", "cpm-communit", "cpm-nand", "cpm-ppv2", "cpm-sdio",
"cpm-mg-domain", "cpm-mg-core", "cpm-xor1", "cpm-xor0", "cpm-gop-dp", "none",
"cpm-pcie_x10", "cpm-pcie_x11", "cpm-pcie_x4", "cpm-pcie-xor", "cpm-sata",
- "cpm-sata-usb", "cpm-main", "cpm-sd-mmc", "none", "none", "cpm-slow-io",
+ "cpm-sata-usb", "cpm-main", "cpm-sd-mmc-gop", "none", "none", "cpm-slow-io",
"cpm-usb3h0", "cpm-usb3h1", "cpm-usb3dev", "cpm-eip150", "cpm-eip197";
};
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv2 0/3] arm64/clk: update Marvell Armada CP110 system controller driver
From: Thomas Petazzoni @ 2016-12-21 10:26 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, linux-clk, Jason Cooper,
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, devicetree,
Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala
Cc: Nadav Haklai, Hanna Hawa, Yehuda Yitschak, linux-arm-kernel,
Thomas Petazzoni
Hello,
This small set of commits updates the Marvell Armada CP110 system
controller driver, its Device Tree binding, and Device Tree
representation, to take into account three new things:
- The clock driver now handles clock n°9 (GOP) as a child of clock
n°18 (controls SD/MMC and GOP)
- The DT representation is adjusted to name clock n°18 "sd-mmc-gop"
instead of just "sd-mmc".
- The clock driver now handles clock n°5 (MG) as a child of clock n°6
(MG_CORE).
This set of commits is some preparation work to add networking support
for Marvell Armada 7K/8K.
I would expect patches 1 and 2 to be taken by the clock maintainers,
and patch 3 be taken by the Marvell EBU maintainers.
Changes since v1:
- Addition of the handling of the MG and MG_CORE clocks (n°5 and
n°6), also needed for the networking support on Marvell Aramda
7K/8K.
Thanks,
Thomas
Thomas Petazzoni (3):
dt-bindings: arm: update Armada CP110 system controller binding
clk: mvebu: adjust clock handling for the CP110 system controller
arm64: dts: marvell: adjust name of sd-mmc-gop clock in syscon
.../bindings/arm/marvell/cp110-system-controller0.txt | 6 +++---
arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi | 2 +-
arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 2 +-
drivers/clk/mvebu/cp110-system-controller.c | 13 +++++++++++--
4 files changed, 16 insertions(+), 7 deletions(-)
--
2.7.4
^ permalink raw reply
* Re: [PATCH v4 1/2] power: supply: add sbs-charger driver
From: Manish Badarkhe @ 2016-12-21 10:09 UTC (permalink / raw)
To: Nicolas Saenz Julienne
Cc: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Mark Rutland, linux-pm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <72292f5b-3f81-f097-a87e-56b58098ff2b-gbiq2sxWoaasTnJN9+BGXg@public.gmane.org>
Hi Nicolas
On Wed, Dec 21, 2016 at 3:00 PM, Nicolas Saenz Julienne
<nicolas.saenz-gbiq2sxWoaasTnJN9+BGXg@public.gmane.org> wrote:
> Hi Manish, thanks for the interest.
>
> On 20/12/16 17:54, Manish Badarkhe wrote:
>> Hi Nicola
>>
> [...]
>>
>> Just some general comment, Can you add some more properties here to
>> know voltage and current?
>
> I assume you are mentioning reading current and voltage values. These
> properties are not supported by the sbs-charger standard. All you can do
> is set (write only) the charging current and charging voltage values.
> Yet in all the boards I've worked with it's the sbs-battery compliant
> chip that takes care of it.
thanks for clarifying me.
>> Also, can you add other properties present in charging status register
>> like POWER_FAIL, VOLTAGE_OR,
>> CURRENT_OR etc.
>
> CURRENT_OR and VOLTAGE_OR relate to wrong values in the charging
> voltage/current registers. Since the sbs-battery chip takes care of it I
> don't see any use for it.
> POWER_FAIL marks there is not enough voltage to charge the battery. I
> don't need that information so I did nothing to integrate it. Having a
> quick look at the power supply defines I don't see were it could easily fit.
thanks for clarifying me.
Regards
Manish Badarkhe
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 net-next 3/3] arm64: dts: marvell: Add ethernet switch definition for the ESPRESSObin
From: Gregory CLEMENT @ 2016-12-21 9:41 UTC (permalink / raw)
To: Romain Perier
Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
Sebastian Hesselbarth, netdev, devicetree, Rob Herring,
Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel, Thomas Petazzoni, Nadav Haklai
In-Reply-To: <20161221090045.474-4-romain.perier@free-electrons.com>
Hi Romain,
On mer., déc. 21 2016, Romain Perier <romain.perier@free-electrons.com> wrote:
> This defines and enables the Marvell ethernet switch MVE886341 on the
> Marvell ESPRESSObin board.
This patch looks OK now.
Applied on mvebu/dt64-4.11
So you can remove it from you next version because I don't want this
patch will be applied through the netdev branch. Indeed I expect more
changes in this file for v4.11 and it will be easier to have the change
in a single branch to avoid the merge conflict.
Thanks,
Gregory
>
> Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
> ---
>
> Changes in v3:
> - Changed the node switch0 to be at 1
> - Removed reg=<1> from the mdio node, finally that's not required
>
> Changes in v2:
> - EXPRESSObin -> ESPRESSObin
> - phy nodes definition must contain the internal bus address after the @
>
> .../boot/dts/marvell/armada-3720-espressobin.dts | 66 ++++++++++++++++++++++
> 1 file changed, 66 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
> index 83178d9..12d9f65 100644
> --- a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
> +++ b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
> @@ -80,3 +80,69 @@
> &usb3 {
> status = "okay";
> };
> +
> +&mdio {
> + switch0: switch0@1 {
> + compatible = "marvell,mv88e6085";
> + #address-cells = <1>;
> + #size-cells = <0>;
> + reg = <1>;
> +
> + dsa,member = <0 0>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> + label = "cpu";
> + ethernet = <ð0>;
> + };
> +
> + port@1 {
> + reg = <1>;
> + label = "wan";
> + phy-handle = <&switch0phy0>;
> + };
> +
> + port@2 {
> + reg = <2>;
> + label = "lan0";
> + phy-handle = <&switch0phy1>;
> + };
> +
> + port@3 {
> + reg = <3>;
> + label = "lan1";
> + phy-handle = <&switch0phy2>;
> + };
> +
> + };
> +
> + mdio {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + switch0phy0: switch0phy0@11 {
> + reg = <0x11>;
> + };
> + switch0phy1: switch0phy1@12 {
> + reg = <0x12>;
> + };
> + switch0phy2: switch0phy2@13 {
> + reg = <0x13>;
> + };
> + };
> + };
> +};
> +
> +ð0 {
> + phy-mode = "rgmii-id";
> + status = "okay";
> +
> + fixed-link {
> + speed = <1000>;
> + full-duplex;
> + };
> +};
> --
> 2.9.3
>
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH v4 1/2] power: supply: add sbs-charger driver
From: Nicolas Saenz Julienne @ 2016-12-21 9:30 UTC (permalink / raw)
To: Manish Badarkhe
Cc: sre-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Mark Rutland, linux-pm-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAKDJKT7WWFd2FSAZ6erXF=t1aaCyR0F5px54TiHw7C4PrnKXSQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Manish, thanks for the interest.
On 20/12/16 17:54, Manish Badarkhe wrote:
> Hi Nicola
>
[...]
>
> Just some general comment, Can you add some more properties here to
> know voltage and current?
I assume you are mentioning reading current and voltage values. These
properties are not supported by the sbs-charger standard. All you can do
is set (write only) the charging current and charging voltage values.
Yet in all the boards I've worked with it's the sbs-battery compliant
chip that takes care of it.
> Also, can you add other properties present in charging status register
> like POWER_FAIL, VOLTAGE_OR,
> CURRENT_OR etc.
CURRENT_OR and VOLTAGE_OR relate to wrong values in the charging
voltage/current registers. Since the sbs-battery chip takes care of it I
don't see any use for it.
POWER_FAIL marks there is not enough voltage to charge the battery. I
don't need that information so I did nothing to integrate it. Having a
quick look at the power supply defines I don't see were it could easily fit.
> Don't know weather it is feasible to add or not and Also, let me know,
> if it is already been covered in
> some part of the code.
>
> Thanks
> Manish Badarkhe
>
Regards,
Nicolas
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 3/7] iio: adc: sun4i-gpadc-iio: add support for A33 thermal sensor
From: Maxime Ripard @ 2016-12-21 9:09 UTC (permalink / raw)
To: Quentin Schulz
Cc: jic23-DgEjT+Ai2ygdnm+yROfE0A, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
wens-jdAy2FN1RRM, lee.jones-QSEj5FYQhm4dnm+yROfE0A,
linux-I+IVW8TIWO2tmTQ+vhA3Yw,
stefan.mavrodiev-Re5JQEeQqe8AvxtiuMwx3w,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8
In-Reply-To: <b668c7f1-99ac-f2ba-eaab-f3c0f9e1cfb2-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 4062 bytes --]
Hi,
On Tue, Dec 20, 2016 at 04:20:04PM +0100, Quentin Schulz wrote:
> >> + select MFD_SUN4I_GPADC if MACH_SUN4I || MACH_SUN5I || MACH_SUN6I
> >
> > Why did you change the depends on to a select?
> >
>
> The "depends on" does not have an if condition but "select" has. See:
> https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt
I think something like that should work:
depends on (MFD_SUN4I_GPADC && MACH_SUN4I) || MACH_SUN8I
> >> + depends on THERMAL_OF || MACH_SUN4I || MACH_SUN5I || MACH_SUN6I
> >
> > So you can't disable THERMAL_OF on MACH_SUN8I?
> >
>
> Not in the current state. devm_thermal_zone_of_sensor_register from
> sun4i_gpadc_probe_dt returns an error if THERMAL_OF is disabled and
> thus, the probe fails. Maybe it should rather fail silently and let the
> user choose whether (s)he wants the thermal framework to be able to read
> data from this driver?
I'm usually not a big fan of inconsistencies in the behaviour of
drivers, but let's wait for the second version to discuss that.
> >> + depends on !TOUCHSCREEN_SUN4I
> >
> > This should be a different patch.
> >
> >> + help
> >> + Say yes here to build support for Allwinner (A10, A13, A31 and A33)
> >> + SoCs GPADC.
> >> +
> >> + The ADC on A10, A13 and A31 provides 4 channels which can be used as
> >> + an ADC or as a touchscreen input and one channel for thermal sensor.
> >> + Their thermal sensor slows down ADC readings and can be disabled by
> >
> > Again, I'm not sure putting all those details in the Kconfig help
> > really helps. This is only going to grow with more and more cases, and
> > this isn't something really helpful anyway.
> >
>
> Are you suggesting to remove completely the paragraph on why it is
> possible to disable CONFIG_THERMAL_OF for the A10, A13 and A31 or only
> to remove the mention to SoCs?
Both actually :)
> >> @@ -246,6 +269,19 @@ static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
> >> static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
> >> {
> >> struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> >> + int ret;
> >> +
> >> + if (info->use_dt) {
> >> + pm_runtime_get_sync(indio_dev->dev.parent);
> >> +
> >> + ret = regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
> >> + if (!ret)
> >> + pm_runtime_mark_last_busy(indio_dev->dev.parent);
> >> +
> >> + pm_runtime_put_autosuspend(indio_dev->dev.parent);
> >> +
> >> + return 0;
> >> + }
> >
> > Why is runtime_pm linked to the DT support or not?
> >
> >>
> >> return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
> >> }
>
> The same runtime_pm functions are called when the driver is not probed
> from DT.
>
> sun4i_gpadc_read will call sun4i_prepare_for_irq which does a
> pm_runtime_get_sync and then at the end of sun4i_gpadc_read,
> pm_runtime_mark_last_busy and pm_runtime_put_autosuspend are called.
>
> I just noticed I forgot to add a comment on this one. The A33
> documentation tells us there is an interrupt for the thermal sensor but
> after struggling with it, it is just false. I validated my guess with
> Allwinner Linux kernel which does not wait an interrupt to read the data
> register of the thermal sensor.
>
> sun4i_gpadc_read always wait for an interrupt before reading data regs,
> so I'm just not calling it and doing the "logic" (reading the data reg
> and interfacing with runtime_pm) directly here in sun4i_gpadc_temp.
>
> I'll add a comment on this one. Maybe I should create a function just
> for the "logic" of the A33 thermal sensor, so it is less weird than
> currently.
Ok. So this is not really about using the DT or not, but rather
whether you're on an A33 or not. You could probably add a broken_irq
field to test against that.
You should probably share the pm_runtim calls too between the two
cases.
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCH v3 net-next 1/3] net: dsa: mv88e6xxx: Don't forbid MDIO I/Os for PHY addr >= num_of_ports
From: Gregory CLEMENT @ 2016-12-21 9:06 UTC (permalink / raw)
To: Romain Perier
Cc: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
Sebastian Hesselbarth, netdev, devicetree, Rob Herring,
Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel, Thomas Petazzoni, Nadav Haklai
In-Reply-To: <20161221090045.474-2-romain.perier@free-electrons.com>
Hi Romain,
it seems there is a remaining typo :)
On mer., déc. 21 2016, Romain Perier <romain.perier@free-electrons.com> wrote:
> Some Marvell ethernet switches have internal ethernet transceivers with
> hardcoded phy addresses. These addresses can be grearer than the number
greater
> of ports or its value might be different than the associated port number.
> This is for example the case for MV88E6341 that has 6 ports and internal
> Port 1 to Port4 PHYs mapped at SMI addresses from 0x11 to 0x14.
>
> This commits fixes the issue by removing the condition in MDIO callbacks.
>
> Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
> ---
>
> Changes in v2:
> - Added tag "Reviewed-by" by Andrew
> - Fixed typo in the commit log
>
> drivers/net/dsa/mv88e6xxx/chip.c | 6 ------
> 1 file changed, 6 deletions(-)
>
> diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
> index b5f0e1e..76d944e 100644
> --- a/drivers/net/dsa/mv88e6xxx/chip.c
> +++ b/drivers/net/dsa/mv88e6xxx/chip.c
> @@ -2881,9 +2881,6 @@ static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
> u16 val;
> int err;
>
> - if (phy >= mv88e6xxx_num_ports(chip))
> - return 0xffff;
> -
> mutex_lock(&chip->reg_lock);
> err = mv88e6xxx_phy_read(chip, phy, reg, &val);
> mutex_unlock(&chip->reg_lock);
> @@ -2896,9 +2893,6 @@ static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
> struct mv88e6xxx_chip *chip = bus->priv;
> int err;
>
> - if (phy >= mv88e6xxx_num_ports(chip))
> - return 0xffff;
> -
> mutex_lock(&chip->reg_lock);
> err = mv88e6xxx_phy_write(chip, phy, reg, val);
> mutex_unlock(&chip->reg_lock);
> --
> 2.9.3
>
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* [PATCH v3 net-next 3/3] arm64: dts: marvell: Add ethernet switch definition for the ESPRESSObin
From: Romain Perier @ 2016-12-21 9:00 UTC (permalink / raw)
To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
Sebastian Hesselbarth, Gregory Clement
Cc: netdev, devicetree, Rob Herring, Ian Campbell, Pawel Moll,
Mark Rutland, Kumar Gala, linux-arm-kernel, Thomas Petazzoni,
Nadav Haklai, Romain Perier
In-Reply-To: <20161221090045.474-1-romain.perier@free-electrons.com>
This defines and enables the Marvell ethernet switch MVE886341 on the
Marvell ESPRESSObin board.
Signed-off-by: Romain Perier <romain.perier@free-electrons.com>
---
Changes in v3:
- Changed the node switch0 to be at 1
- Removed reg=<1> from the mdio node, finally that's not required
Changes in v2:
- EXPRESSObin -> ESPRESSObin
- phy nodes definition must contain the internal bus address after the @
.../boot/dts/marvell/armada-3720-espressobin.dts | 66 ++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
index 83178d9..12d9f65 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-espressobin.dts
@@ -80,3 +80,69 @@
&usb3 {
status = "okay";
};
+
+&mdio {
+ switch0: switch0@1 {
+ compatible = "marvell,mv88e6085";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ dsa,member = <0 0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ label = "cpu";
+ ethernet = <ð0>;
+ };
+
+ port@1 {
+ reg = <1>;
+ label = "wan";
+ phy-handle = <&switch0phy0>;
+ };
+
+ port@2 {
+ reg = <2>;
+ label = "lan0";
+ phy-handle = <&switch0phy1>;
+ };
+
+ port@3 {
+ reg = <3>;
+ label = "lan1";
+ phy-handle = <&switch0phy2>;
+ };
+
+ };
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ switch0phy0: switch0phy0@11 {
+ reg = <0x11>;
+ };
+ switch0phy1: switch0phy1@12 {
+ reg = <0x12>;
+ };
+ switch0phy2: switch0phy2@13 {
+ reg = <0x13>;
+ };
+ };
+ };
+};
+
+ð0 {
+ phy-mode = "rgmii-id";
+ status = "okay";
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+};
--
2.9.3
^ permalink raw reply related
* [PATCH v3 net-next 2/3] net: dsa: mv88e6xxx: Add support for ethernet switch 88E6341/88E6141
From: Romain Perier @ 2016-12-21 9:00 UTC (permalink / raw)
To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
Sebastian Hesselbarth, Gregory Clement
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni, Nadav Haklai, Romain Perier
In-Reply-To: <20161221090045.474-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
The Marvell 88E6341 device is single-chip, 6-port ethernet switch with
four integrated 10/100/1000Mbps ethernet transceivers and one high speed
SerDes interfaces. It is compatible with switches of family 88E6352.
This commit adds basic support for this switch by describing its
capabilities to the driver.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Reviewed-by: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
---
Changes in v3:
- Added tag "Reviewed-by" by Andrew
Changes in v2:
- Add a dedicated data structure for the operations of the 88E6341
- Re-ordered PORT_SWITCH_ID_PROD_NUM_6341 in alphabetic order with other
macros
drivers/net/dsa/mv88e6xxx/chip.c | 42 +++++++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 4 +++-
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 76d944e..5e97dc4 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -3625,6 +3625,34 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
.reset = mv88e6352_g1_reset,
};
+static const struct mv88e6xxx_ops mv88e6341_ops = {
+ /* MV88E6XXX_FAMILY_6352 */
+ .get_eeprom = mv88e6xxx_g2_get_eeprom16,
+ .set_eeprom = mv88e6xxx_g2_set_eeprom16,
+ .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
+ .phy_read = mv88e6xxx_g2_smi_phy_read,
+ .phy_write = mv88e6xxx_g2_smi_phy_write,
+ .port_set_link = mv88e6xxx_port_set_link,
+ .port_set_duplex = mv88e6xxx_port_set_duplex,
+ .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
+ .port_set_speed = mv88e6352_port_set_speed,
+ .port_tag_remap = mv88e6095_port_tag_remap,
+ .port_set_frame_mode = mv88e6351_port_set_frame_mode,
+ .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns,
+ .port_set_ether_type = mv88e6351_port_set_ether_type,
+ .port_jumbo_config = mv88e6165_port_jumbo_config,
+ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+ .port_pause_config = mv88e6097_port_pause_config,
+ .stats_snapshot = mv88e6320_g1_stats_snapshot,
+ .stats_get_sset_count = mv88e6095_stats_get_sset_count,
+ .stats_get_strings = mv88e6095_stats_get_strings,
+ .stats_get_stats = mv88e6095_stats_get_stats,
+ .g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
+ .g1_set_egress_port = mv88e6095_g1_set_egress_port,
+ .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
+ .reset = mv88e6352_g1_reset,
+};
+
static const struct mv88e6xxx_ops mv88e6350_ops = {
/* MV88E6XXX_FAMILY_6351 */
.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
@@ -4086,6 +4114,20 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.ops = &mv88e6321_ops,
},
+ [MV88E6341] = {
+ .prod_num = PORT_SWITCH_ID_PROD_NUM_6341,
+ .family = MV88E6XXX_FAMILY_6352,
+ .name = "Marvell 88E6341",
+ .num_databases = 4096,
+ .num_ports = 6,
+ .port_base_addr = 0x10,
+ .global1_addr = 0x1b,
+ .age_time_coeff = 15000,
+ .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .flags = MV88E6XXX_FLAGS_FAMILY_6352,
+ .ops = &mv88e6341_ops,
+ },
+
[MV88E6350] = {
.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
.family = MV88E6XXX_FAMILY_6351,
diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
index af54bae..cb55fdb 100644
--- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h
@@ -100,6 +100,7 @@
#define PORT_SWITCH_ID_PROD_NUM_6240 0x240
#define PORT_SWITCH_ID_PROD_NUM_6290 0x290
#define PORT_SWITCH_ID_PROD_NUM_6321 0x310
+#define PORT_SWITCH_ID_PROD_NUM_6341 0x340
#define PORT_SWITCH_ID_PROD_NUM_6352 0x352
#define PORT_SWITCH_ID_PROD_NUM_6350 0x371
#define PORT_SWITCH_ID_PROD_NUM_6351 0x375
@@ -432,6 +433,7 @@ enum mv88e6xxx_model {
MV88E6290,
MV88E6320,
MV88E6321,
+ MV88E6341,
MV88E6350,
MV88E6351,
MV88E6352,
@@ -448,7 +450,7 @@ enum mv88e6xxx_family {
MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */
MV88E6XXX_FAMILY_6320, /* 6320 6321 */
MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */
- MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */
+ MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6341 6352 */
MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */
};
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 net-next 1/3] net: dsa: mv88e6xxx: Don't forbid MDIO I/Os for PHY addr >= num_of_ports
From: Romain Perier @ 2016-12-21 9:00 UTC (permalink / raw)
To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
Sebastian Hesselbarth, Gregory Clement
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni, Nadav Haklai, Romain Perier
In-Reply-To: <20161221090045.474-1-romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Some Marvell ethernet switches have internal ethernet transceivers with
hardcoded phy addresses. These addresses can be grearer than the number
of ports or its value might be different than the associated port number.
This is for example the case for MV88E6341 that has 6 ports and internal
Port 1 to Port4 PHYs mapped at SMI addresses from 0x11 to 0x14.
This commits fixes the issue by removing the condition in MDIO callbacks.
Signed-off-by: Romain Perier <romain.perier-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Reviewed-by: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
---
Changes in v2:
- Added tag "Reviewed-by" by Andrew
- Fixed typo in the commit log
drivers/net/dsa/mv88e6xxx/chip.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index b5f0e1e..76d944e 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -2881,9 +2881,6 @@ static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
u16 val;
int err;
- if (phy >= mv88e6xxx_num_ports(chip))
- return 0xffff;
-
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_phy_read(chip, phy, reg, &val);
mutex_unlock(&chip->reg_lock);
@@ -2896,9 +2893,6 @@ static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
struct mv88e6xxx_chip *chip = bus->priv;
int err;
- if (phy >= mv88e6xxx_num_ports(chip))
- return 0xffff;
-
mutex_lock(&chip->reg_lock);
err = mv88e6xxx_phy_write(chip, phy, reg, val);
mutex_unlock(&chip->reg_lock);
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 net-next 0/3] Add support for the ethernet switch on the ESPRESSObin
From: Romain Perier @ 2016-12-21 9:00 UTC (permalink / raw)
To: Andrew Lunn, Vivien Didelot, Florian Fainelli, Jason Cooper,
Sebastian Hesselbarth, Gregory Clement
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Thomas Petazzoni, Nadav Haklai, Romain Perier
This set of patches adds support for the Marvell ethernet switch 88E6341.
It also add the devicetree definition of this switch to the DT board.
Romain Perier (3):
net: dsa: mv88e6xxx: Don't forbid MDIO I/Os for PHY addr >=
num_of_ports
net: dsa: mv88e6xxx: Add support for ethernet switch 88E6341/88E6141
arm64: dts: marvell: Add ethernet switch definition for the
ESPRESSObin
.../boot/dts/marvell/armada-3720-espressobin.dts | 66 ++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/chip.c | 48 ++++++++++++++--
drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 4 +-
3 files changed, 111 insertions(+), 7 deletions(-)
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 3/3] arm64: dts: marvell: Add ethernet switch definition for the ESPRESSObin
From: Romain Perier @ 2016-12-21 8:57 UTC (permalink / raw)
To: Andrew Lunn
Cc: Vivien Didelot, Florian Fainelli, Jason Cooper,
Sebastian Hesselbarth, Gregory Clement, netdev, devicetree,
Rob Herring, Ian Campbell, Pawel Moll, Mark Rutland, Kumar Gala,
linux-arm-kernel, Thomas Petazzoni, Nadav Haklai
In-Reply-To: <20161220161714.GD30952@lunn.ch>
Hi,
Le 20/12/2016 à 17:17, Andrew Lunn a écrit :
>>>> + mdio {
>>>> + #address-cells = <1>;
>>>> + #size-cells = <0>;
>>>> + reg = <1>;
>>>
>>> what is this reg value for?
>>>
>>> Andrew
>>>
>>
>> It was required to avoid a warning thrown by the mdio subsystem
>
> Do you remember what the warning was?
>
> This seems odd to me. I don't see why a reg is needed here.
>
> Thanks
> Andrew
>
Mhhh, in fact, I did changes to this mdio node, I relocated it...
the warning was probably before the relocation, because I no longer see
it. So I have removed this reg value.
Thanks,
Romain
^ permalink raw reply
* [PATCH v2 7/7] media: platform: rcar_drif: Add DRIF support
From: Ramesh Shanmugasundaram @ 2016-12-21 8:10 UTC (permalink / raw)
To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
devicetree, linux-renesas-soc, Ramesh Shanmugasundaram
In-Reply-To: <1482307838-47415-1-git-send-email-ramesh.shanmugasundaram@bp.renesas.com>
This patch adds Digital Radio Interface (DRIF) support to R-Car Gen3 SoCs.
The driver exposes each instance of DRIF as a V4L2 SDR device. A DRIF
device represents a channel and each channel can have one or two
sub-channels respectively depending on the target board.
DRIF supports only Rx functionality. It receives samples from a RF
frontend tuner chip it is interfaced with. The combination of DRIF and the
tuner device, which is registered as a sub-device, determines the receive
sample rate and format.
In order to be compliant as a V4L2 SDR device, DRIF needs to bind with
the tuner device, which can be provided by a third party vendor. DRIF acts
as a slave device and the tuner device acts as a master transmitting the
samples. The driver allows asynchronous binding of a tuner device that
is registered as a v4l2 sub-device. The driver can learn about the tuner
it is interfaced with based on port endpoint properties of the device in
device tree. The V4L2 SDR device inherits the controls exposed by the
tuner device.
The device can also be configured to use either one or both of the data
pins at runtime based on the master (tuner) configuration.
Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
drivers/media/platform/Kconfig | 25 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/rcar_drif.c | 1593 ++++++++++++++++++++++++++++++++++++
3 files changed, 1619 insertions(+)
create mode 100644 drivers/media/platform/rcar_drif.c
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index d944421..d288146 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -421,3 +421,28 @@ menuconfig DVB_PLATFORM_DRIVERS
if DVB_PLATFORM_DRIVERS
source "drivers/media/platform/sti/c8sectpfe/Kconfig"
endif #DVB_PLATFORM_DRIVERS
+
+menuconfig SDR_PLATFORM_DRIVERS
+ bool "SDR platform devices"
+ depends on MEDIA_SDR_SUPPORT
+ default n
+ ---help---
+ Say Y here to enable support for platform-specific SDR Drivers.
+
+if SDR_PLATFORM_DRIVERS
+
+config VIDEO_RCAR_DRIF
+ tristate "Renesas Digitial Radio Interface (DRIF)"
+ depends on VIDEO_V4L2 && HAS_DMA
+ depends on ARCH_RENESAS
+ select VIDEOBUF2_VMALLOC
+ ---help---
+ Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital
+ Radio Interface that interfaces with an RF front end chip. It is a
+ receiver of digital data which uses DMA to transfer received data to
+ a configured location for an application to use.
+
+ To compile this driver as a module, choose M here; the module
+ will be called rcar_drif.
+
+endif # SDR_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 5b3cb27..1c2daaf 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
+obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o
obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o
obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o
obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c
new file mode 100644
index 0000000..0827bd8
--- /dev/null
+++ b/drivers/media/platform/rcar_drif.c
@@ -0,0 +1,1593 @@
+/*
+ * R-Car Gen3 Digital Radio Interface (DRIF) driver
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ */
+
+/*
+ * The R-Car DRIF is a receive only MSIOF like controller with an
+ * external master device driving the SCK. It receives data into a FIFO,
+ * then this driver uses the SYS-DMAC engine to move the data from
+ * the device to memory.
+ *
+ * Each DRIF channel DRIFx (as per datasheet) contains two internal
+ * channels DRIFx0 & DRIFx1 within itself with each having its own resources
+ * like module clk, register set, irq and dma. These internal channels share
+ * common CLK & SYNC from master. The two data pins D0 & D1 shall be
+ * considered to represent the two internal channels. This internal split
+ * is not visible to the master device.
+ *
+ * Depending on the master device, a DRIF channel can use
+ * (1) both internal channels (D0 & D1) to receive data in parallel (or)
+ * (2) one internal channel (D0 or D1) to receive data
+ *
+ * The primary design goal of this controller is to act as Digitial Radio
+ * Interface that receives digital samples from a tuner device. Hence the
+ * driver exposes the device as a V4L2 SDR device. In order to qualify as
+ * a V4L2 SDR device, it should possess tuner interface as mandated by the
+ * framework. This driver expects a tuner driver (sub-device) to bind
+ * asynchronously with this device and the combined drivers shall expose
+ * a V4L2 compliant SDR device. The DRIF driver is independent of the
+ * tuner vendor.
+ */
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/ioctl.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+
+/* DRIF register offsets */
+#define RCAR_DRIF_SITMDR1 0x00
+#define RCAR_DRIF_SITMDR2 0x04
+#define RCAR_DRIF_SITMDR3 0x08
+#define RCAR_DRIF_SIRMDR1 0x10
+#define RCAR_DRIF_SIRMDR2 0x14
+#define RCAR_DRIF_SIRMDR3 0x18
+#define RCAR_DRIF_SICTR 0x28
+#define RCAR_DRIF_SIFCTR 0x30
+#define RCAR_DRIF_SISTR 0x40
+#define RCAR_DRIF_SIIER 0x44
+#define RCAR_DRIF_SIRFDR 0x60
+
+#define RCAR_DRIF_RFOVF BIT(3) /* Receive FIFO overflow */
+#define RCAR_DRIF_RFUDF BIT(4) /* Receive FIFO underflow */
+#define RCAR_DRIF_RFSERR BIT(5) /* Receive frame sync error */
+#define RCAR_DRIF_REOF BIT(7) /* Frame reception end */
+#define RCAR_DRIF_RDREQ BIT(12) /* Receive data xfer req */
+#define RCAR_DRIF_RFFUL BIT(13) /* Receive FIFO full */
+
+/* SIRMDR1 */
+#define RCAR_DRIF_SIRMDR1_SYNCMD_FRAME (0 << 28)
+#define RCAR_DRIF_SIRMDR1_SYNCMD_LR (3 << 28)
+
+#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH (0 << 25)
+#define RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW (1 << 25)
+
+#define RCAR_DRIF_SIRMDR1_MSB_FIRST (0 << 24)
+#define RCAR_DRIF_SIRMDR1_LSB_FIRST (1 << 24)
+
+#define RCAR_DRIF_SIRMDR1_DTDL_0 (0 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_1 (1 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_2 (2 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_0PT5 (5 << 20)
+#define RCAR_DRIF_SIRMDR1_DTDL_1PT5 (6 << 20)
+
+#define RCAR_DRIF_SIRMDR1_SYNCDL_0 (0 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_1 (1 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_2 (2 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_3 (3 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_0PT5 (5 << 20)
+#define RCAR_DRIF_SIRMDR1_SYNCDL_1PT5 (6 << 20)
+
+#define RCAR_DRIF_MDR_GRPCNT(n) (((n) - 1) << 30)
+#define RCAR_DRIF_MDR_BITLEN(n) (((n) - 1) << 24)
+#define RCAR_DRIF_MDR_WDCNT(n) (((n) - 1) << 16)
+
+/* Hidden Transmit register that controls CLK & SYNC */
+#define RCAR_DRIF_SITMDR1_PCON BIT(30)
+
+#define RCAR_DRIF_SICTR_RX_RISING_EDGE BIT(26)
+#define RCAR_DRIF_SICTR_RX_EN BIT(8)
+#define RCAR_DRIF_SICTR_RESET BIT(0)
+
+/* Constants */
+#define RCAR_DRIF_MAX_NUM_HWBUFS 32
+#define RCAR_DRIF_MAX_DEVS 4
+#define RCAR_DRIF_DEFAULT_NUM_HWBUFS 16
+#define RCAR_DRIF_DEFAULT_HWBUF_SIZE (4 * PAGE_SIZE)
+#define RCAR_DRIF_MAX_CHANNEL 2
+#define RCAR_SDR_BUFFER_SIZE SZ_64K
+
+/* Internal buffer status flags */
+#define RCAR_DRIF_BUF_DONE BIT(0) /* DMA completed */
+#define RCAR_DRIF_BUF_OVERFLOW BIT(1) /* Overflow detected */
+
+#define to_rcar_drif_buf_pair(sdr, ch_num, idx) (sdr->ch[!(ch_num)]->buf[idx])
+
+static const unsigned int num_hwbufs = 32;
+#define for_each_rcar_drif_channel(ch, ch_mask) \
+ for_each_set_bit(ch, ch_mask, RCAR_DRIF_MAX_CHANNEL)
+
+/* Debug */
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activate debug info");
+
+#define rdrif_dbg(level, sdr, fmt, arg...) \
+ v4l2_dbg(level, debug, &sdr->v4l2_dev, fmt, ## arg)
+
+#define rdrif_err(sdr, fmt, arg...) \
+ dev_err(sdr->v4l2_dev.dev, fmt, ## arg)
+
+/* Stream formats */
+struct rcar_drif_format {
+ u32 pixelformat;
+ u32 buffersize;
+ u32 wdlen;
+ u32 num_ch;
+};
+
+/* Format descriptions for capture and preview */
+static const struct rcar_drif_format formats[] = {
+ {
+ .pixelformat = V4L2_SDR_FMT_PCU16BE,
+ .buffersize = RCAR_SDR_BUFFER_SIZE,
+ .wdlen = 16,
+ .num_ch = 2,
+ },
+ {
+ .pixelformat = V4L2_SDR_FMT_PCU18BE,
+ .buffersize = RCAR_SDR_BUFFER_SIZE,
+ .wdlen = 18,
+ .num_ch = 2,
+ },
+ {
+ .pixelformat = V4L2_SDR_FMT_PCU20BE,
+ .buffersize = RCAR_SDR_BUFFER_SIZE,
+ .wdlen = 20,
+ .num_ch = 2,
+ },
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
+
+/* Buffer for a received frame from one or both internal channels */
+struct rcar_drif_frame_buf {
+ /* Common v4l buffer stuff -- must be first */
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+struct rcar_drif_async_subdev {
+ struct v4l2_subdev *sd;
+ struct v4l2_async_subdev asd;
+};
+
+/* DMA buffer */
+struct rcar_drif_hwbuf {
+ void *addr; /* CPU-side address */
+ unsigned int status; /* Buffer status flags */
+};
+
+/* Internal channel */
+struct rcar_drif {
+ struct rcar_drif_sdr *sdr; /* Group device */
+ struct platform_device *pdev; /* Channel's pdev */
+ void __iomem *base; /* Base register address */
+ resource_size_t start; /* I/O resource offset */
+ struct dma_chan *dmach; /* Reserved DMA channel */
+ struct clk *clkp; /* Module clock */
+ struct rcar_drif_hwbuf *buf[RCAR_DRIF_MAX_NUM_HWBUFS];
+ dma_addr_t dma_handle; /* Handle for all bufs */
+ unsigned int num; /* Channel number */
+ bool acting_sdr; /* Channel acting as SDR device */
+};
+
+/* DRIF V4L2 SDR */
+struct rcar_drif_sdr {
+ struct device *dev; /* Platform device */
+ struct video_device *vdev; /* V4L2 SDR device */
+ struct v4l2_device v4l2_dev; /* V4L2 device */
+
+ /* Videobuf2 queue and queued buffers list */
+ struct vb2_queue vb_queue;
+ struct list_head queued_bufs;
+ spinlock_t queued_bufs_lock; /* Protects queued_bufs */
+
+ struct mutex v4l2_mutex; /* To serialize ioctls */
+ struct mutex vb_queue_mutex; /* To serialize streaming ioctls */
+ struct v4l2_ctrl_handler ctrl_hdl; /* SDR control handler */
+ struct v4l2_async_notifier notifier; /* For subdev (tuner) */
+
+ /* Current V4L2 SDR format array index */
+ unsigned int fmt_idx;
+
+ /* Device tree SYNC properties */
+ u32 mdr1;
+
+ /* Internals */
+ struct rcar_drif *ch[RCAR_DRIF_MAX_CHANNEL]; /* DRIFx0,1 */
+ unsigned long hw_ch_mask; /* Enabled channels per DT */
+ unsigned long cur_ch_mask; /* Used channels for an SDR FMT */
+ u32 num_hw_ch; /* Num of DT enabled channels */
+ u32 num_cur_ch; /* Num of used channels */
+ u32 hwbuf_size; /* Each DMA buffer size */
+ u32 produced; /* Buffers produced by sdr dev */
+};
+
+/* Allocate buffer context */
+static int rcar_drif_alloc_bufctxt(struct rcar_drif_sdr *sdr)
+{
+ struct rcar_drif_hwbuf *bufctx;
+ unsigned int i, idx;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ bufctx = kcalloc(num_hwbufs, sizeof(*bufctx), GFP_KERNEL);
+ if (!bufctx)
+ return -ENOMEM;
+
+ for (idx = 0; idx < num_hwbufs; idx++)
+ sdr->ch[i]->buf[idx] = bufctx + idx;
+ }
+ return 0;
+}
+
+/* Release buffer context */
+static void rcar_drif_release_bufctxt(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ kfree(sdr->ch[i]->buf[0]);
+ sdr->ch[i]->buf[0] = NULL;
+ }
+}
+
+/* Release DMA channel */
+static void rcar_drif_release_dmachannel(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+ if (sdr->ch[i]->dmach) {
+ dma_release_channel(sdr->ch[i]->dmach);
+ sdr->ch[i]->dmach = NULL;
+ }
+}
+
+/* Allocate DMA channel */
+static int rcar_drif_alloc_dmachannel(struct rcar_drif_sdr *sdr)
+{
+ struct dma_slave_config dma_cfg;
+ unsigned int i;
+ int ret = -ENODEV;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ struct rcar_drif *ch = sdr->ch[i];
+
+ ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx");
+ if (!ch->dmach) {
+ rdrif_err(sdr, "ch%u: dma channel req failed\n", i);
+ goto dmach_error;
+ }
+
+ /* Configure slave */
+ memset(&dma_cfg, 0, sizeof(dma_cfg));
+ dma_cfg.src_addr = (phys_addr_t)(ch->start + RCAR_DRIF_SIRFDR);
+ dma_cfg.dst_addr = 0;
+ dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ ret = dmaengine_slave_config(ch->dmach, &dma_cfg);
+ if (ret) {
+ rdrif_err(sdr, "ch%u: dma slave config failed\n", i);
+ goto dmach_error;
+ }
+ }
+ return 0;
+
+dmach_error:
+ rcar_drif_release_dmachannel(sdr);
+ return ret;
+}
+
+/* Release queued vb2 buffers */
+static void rcar_drif_release_queued_bufs(struct rcar_drif_sdr *sdr,
+ enum vb2_buffer_state state)
+{
+ struct rcar_drif_frame_buf *fbuf, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+ list_for_each_entry_safe(fbuf, tmp, &sdr->queued_bufs, list) {
+ list_del(&fbuf->list);
+ vb2_buffer_done(&fbuf->vb.vb2_buf, state);
+ }
+ spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+}
+
+/* Set MDR defaults */
+static inline void rcar_drif_set_mdr1(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ /* Set defaults for both internal channels */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ /* Refer MSIOF section in manual for this register setting */
+ writel(RCAR_DRIF_SITMDR1_PCON,
+ sdr->ch[i]->base + RCAR_DRIF_SITMDR1);
+
+ /* Setup MDR1 value */
+ writel(sdr->mdr1, sdr->ch[i]->base + RCAR_DRIF_SIRMDR1);
+
+ rdrif_dbg(2, sdr, "ch%u: mdr1 = 0x%08x",
+ i, readl(sdr->ch[i]->base + RCAR_DRIF_SIRMDR1));
+ }
+}
+
+/* Extract bitlen and wdcnt from given word length */
+static int rcar_drif_convert_wdlen(struct rcar_drif_sdr *sdr,
+ u32 wdlen, u32 *bitlen, u32 *wdcnt)
+{
+ unsigned int i, nr_wds;
+
+ /* FIFO register size is 32 bits */
+ for (i = 0; i < 32; i++) {
+ nr_wds = wdlen % (32 - i);
+ if (nr_wds == 0) {
+ *bitlen = 32 - i;
+ *wdcnt = wdlen / *bitlen;
+ break;
+ }
+ }
+
+ /* Sanity check range */
+ if (i == 32 || !(*bitlen >= 8 && *bitlen <= 32) ||
+ !(*wdcnt >= 1 && *wdcnt <= 64)) {
+ rdrif_err(sdr, "invalid wdlen %u configured\n", wdlen);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Set DRIF receive format */
+static int rcar_drif_set_format(struct rcar_drif_sdr *sdr)
+{
+ u32 bitlen, wdcnt, wdlen;
+ unsigned int i;
+ int ret = -EINVAL;
+
+ wdlen = formats[sdr->fmt_idx].wdlen;
+ rdrif_dbg(2, sdr, "setfmt: idx %u, wdlen %u, num_ch %u\n",
+ sdr->fmt_idx, wdlen, formats[sdr->fmt_idx].num_ch);
+
+ /* Sanity check */
+ if (formats[sdr->fmt_idx].num_ch > sdr->num_cur_ch) {
+ rdrif_err(sdr, "fmt idx %u current ch %u mismatch\n",
+ sdr->fmt_idx, sdr->num_cur_ch);
+ return ret;
+ }
+
+ /* Get bitlen & wdcnt from wdlen */
+ ret = rcar_drif_convert_wdlen(sdr, wdlen, &bitlen, &wdcnt);
+ if (ret)
+ return ret;
+
+ /* Setup group, bitlen & wdcnt */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ u32 mdr;
+
+ /* Two groups */
+ mdr = RCAR_DRIF_MDR_GRPCNT(2) | RCAR_DRIF_MDR_BITLEN(bitlen) |
+ RCAR_DRIF_MDR_WDCNT(wdcnt);
+ writel(mdr, sdr->ch[i]->base + RCAR_DRIF_SIRMDR2);
+
+ mdr = RCAR_DRIF_MDR_BITLEN(bitlen) | RCAR_DRIF_MDR_WDCNT(wdcnt);
+ writel(mdr, sdr->ch[i]->base + RCAR_DRIF_SIRMDR3);
+
+ rdrif_dbg(2, sdr, "ch%u: new mdr[2,3] = 0x%08x, 0x%08x\n",
+ i, readl(sdr->ch[i]->base + RCAR_DRIF_SIRMDR2),
+ readl(sdr->ch[i]->base + RCAR_DRIF_SIRMDR3));
+ }
+ return ret;
+}
+
+/* Release DMA buffers */
+static void rcar_drif_release_buf(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ struct rcar_drif *ch = sdr->ch[i];
+
+ /* First entry contains the dma buf ptr */
+ if (ch->buf[0] && ch->buf[0]->addr) {
+ dma_free_coherent(&ch->pdev->dev,
+ sdr->hwbuf_size * num_hwbufs,
+ ch->buf[0]->addr, ch->dma_handle);
+ ch->buf[0]->addr = NULL;
+ }
+ }
+}
+
+/* Request DMA buffers */
+static int rcar_drif_request_buf(struct rcar_drif_sdr *sdr)
+{
+ int ret = -ENOMEM;
+ unsigned int i, j;
+ void *addr;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ struct rcar_drif *ch = sdr->ch[i];
+
+ /* Allocate DMA buffers */
+ addr = dma_alloc_coherent(&ch->pdev->dev,
+ sdr->hwbuf_size * num_hwbufs,
+ &ch->dma_handle, GFP_KERNEL);
+ if (!addr) {
+ rdrif_err(sdr,
+ "ch%u: dma alloc failed. num_hwbufs %u size %u\n",
+ i, num_hwbufs, sdr->hwbuf_size);
+ goto alloc_error;
+ }
+
+ /* Split the chunk and populate bufctxt */
+ for (j = 0; j < num_hwbufs; j++) {
+ ch->buf[j]->addr = addr + (j * sdr->hwbuf_size);
+ ch->buf[j]->status = 0;
+ }
+ }
+
+ return 0;
+
+alloc_error:
+ return ret;
+}
+
+/* Setup vb_queue minimum buffer requirements */
+static int rcar_drif_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+
+ /* Need at least 16 buffers */
+ if (vq->num_buffers + *num_buffers < 16)
+ *num_buffers = 16 - vq->num_buffers;
+
+ *num_planes = 1;
+ sizes[0] = PAGE_ALIGN(formats[sdr->fmt_idx].buffersize);
+
+ rdrif_dbg(2, sdr, "num_bufs %d sizes[0] %d\n", *num_buffers, sizes[0]);
+ return 0;
+}
+
+/* Enqueue buffer */
+static void rcar_drif_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vb->vb2_queue);
+ struct rcar_drif_frame_buf *fbuf =
+ container_of(vbuf, struct rcar_drif_frame_buf, vb);
+ unsigned long flags;
+
+ rdrif_dbg(2, sdr, "buf_queue idx %u\n", vb->index);
+ spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+ list_add_tail(&fbuf->list, &sdr->queued_bufs);
+ spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+}
+
+/* Get a frame buf from list */
+static struct rcar_drif_frame_buf *
+rcar_drif_get_fbuf(struct rcar_drif_sdr *sdr)
+{
+ struct rcar_drif_frame_buf *fbuf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdr->queued_bufs_lock, flags);
+ fbuf = list_first_entry_or_null(&sdr->queued_bufs, struct
+ rcar_drif_frame_buf, list);
+ if (!fbuf) {
+ /*
+ * App is late in enqueing buffers. Samples lost & there will
+ * be a gap in sequence number when app recovers
+ */
+ rdrif_dbg(1, sdr, "\napp late: prod %u\n", sdr->produced);
+ sdr->produced++; /* Increment the produced count anyway */
+ spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+ return NULL;
+ }
+ list_del(&fbuf->list);
+ spin_unlock_irqrestore(&sdr->queued_bufs_lock, flags);
+
+ return fbuf;
+}
+
+static inline bool rcar_drif_buf_pairs_done(struct rcar_drif_hwbuf *buf1,
+ struct rcar_drif_hwbuf *buf2)
+{
+ return (buf1->status & buf2->status & RCAR_DRIF_BUF_DONE);
+}
+
+/* Channel DMA complete */
+static void rcar_drif_channel_complete(struct rcar_drif *ch, u32 idx)
+{
+ u32 str;
+
+ ch->buf[idx]->status |= RCAR_DRIF_BUF_DONE;
+
+ /* Check for DRIF errors */
+ str = readl(ch->base + RCAR_DRIF_SISTR);
+ if (unlikely(str & RCAR_DRIF_RFOVF)) {
+ /* Writing the same clears it */
+ writel(str, ch->base + RCAR_DRIF_SISTR);
+
+ /* Overflow: some samples are lost */
+ ch->buf[idx]->status |= RCAR_DRIF_BUF_OVERFLOW;
+ }
+}
+
+/* Deliver buffer to user */
+static void rcar_drif_deliver_buf(struct rcar_drif *ch)
+{
+ struct rcar_drif_sdr *sdr = ch->sdr;
+ u32 idx = sdr->produced % num_hwbufs;
+ struct rcar_drif_frame_buf *fbuf;
+ bool overflow = false;
+
+ rcar_drif_channel_complete(ch, idx);
+
+ if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL) {
+ struct rcar_drif_hwbuf *bufi, *bufq;
+
+ if (ch->num) {
+ bufi = to_rcar_drif_buf_pair(sdr, ch->num, idx);
+ bufq = ch->buf[idx];
+ } else {
+ bufi = ch->buf[idx];
+ bufq = to_rcar_drif_buf_pair(sdr, ch->num, idx);
+ }
+
+ /* Check if both DMA buffers are done */
+ if (!rcar_drif_buf_pairs_done(bufi, bufq))
+ return;
+
+ /* Clear buf done status */
+ bufi->status &= ~RCAR_DRIF_BUF_DONE;
+ bufq->status &= ~RCAR_DRIF_BUF_DONE;
+
+ /* Get fbuf */
+ fbuf = rcar_drif_get_fbuf(sdr);
+ if (!fbuf)
+ return;
+
+ memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0),
+ bufi->addr, sdr->hwbuf_size);
+ memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0) + sdr->hwbuf_size,
+ bufq->addr, sdr->hwbuf_size);
+
+ if ((bufi->status | bufq->status) & RCAR_DRIF_BUF_OVERFLOW) {
+ overflow = true;
+ /* Clear the flag in status */
+ bufi->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+ bufq->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+ }
+ } else {
+ struct rcar_drif_hwbuf *bufiq;
+
+ /* Get fbuf */
+ fbuf = rcar_drif_get_fbuf(sdr);
+ if (!fbuf)
+ return;
+
+ bufiq = ch->buf[idx];
+
+ memcpy(vb2_plane_vaddr(&fbuf->vb.vb2_buf, 0),
+ bufiq->addr, sdr->hwbuf_size);
+
+ if (bufiq->status & RCAR_DRIF_BUF_OVERFLOW) {
+ overflow = true;
+ /* Clear the flag in status */
+ bufiq->status &= ~RCAR_DRIF_BUF_OVERFLOW;
+ }
+ }
+
+ rdrif_dbg(2, sdr, "ch%u: prod %u\n", ch->num, sdr->produced);
+
+ fbuf->vb.field = V4L2_FIELD_NONE;
+ fbuf->vb.sequence = sdr->produced++;
+ fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
+ vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0,
+ formats[sdr->fmt_idx].buffersize);
+
+ /* Set error state on overflow */
+ if (overflow)
+ vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+/* DMA callback for each stage */
+static void rcar_drif_dma_complete(void *dma_async_param)
+{
+ struct rcar_drif *ch = dma_async_param;
+ struct rcar_drif_sdr *sdr = ch->sdr;
+
+ mutex_lock(&sdr->vb_queue_mutex);
+
+ /* DMA can be terminated while the callback was waiting on lock */
+ if (!vb2_is_streaming(&sdr->vb_queue))
+ goto stopped;
+
+ rcar_drif_deliver_buf(ch);
+stopped:
+ mutex_unlock(&sdr->vb_queue_mutex);
+}
+
+static int rcar_drif_qbuf(struct rcar_drif *ch)
+{
+ struct rcar_drif_sdr *sdr = ch->sdr;
+ dma_addr_t addr = ch->dma_handle;
+ struct dma_async_tx_descriptor *rxd;
+ dma_cookie_t cookie;
+ int ret = -EIO;
+
+ /* Setup cyclic DMA with given buffers */
+ rxd = dmaengine_prep_dma_cyclic(ch->dmach, addr,
+ sdr->hwbuf_size * num_hwbufs,
+ sdr->hwbuf_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!rxd) {
+ rdrif_err(sdr, "ch%u: prep dma cyclic failed\n", ch->num);
+ return ret;
+ }
+
+ /* Submit descriptor */
+ rxd->callback = rcar_drif_dma_complete;
+ rxd->callback_param = ch;
+ cookie = dmaengine_submit(rxd);
+ if (dma_submit_error(cookie)) {
+ rdrif_err(sdr, "ch%u: dma submit failed\n", ch->num);
+ return ret;
+ }
+
+ dma_async_issue_pending(ch->dmach);
+ return 0;
+}
+
+/* Enable reception */
+static int rcar_drif_enable_rx(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+ u32 ctr;
+ int ret;
+
+ /*
+ * When both internal channels are enabled, they can be synchronized
+ * only by the master
+ */
+
+ /* Enable receive */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ctr = readl(sdr->ch[i]->base + RCAR_DRIF_SICTR);
+ ctr |= (RCAR_DRIF_SICTR_RX_RISING_EDGE |
+ RCAR_DRIF_SICTR_RX_EN);
+ writel(ctr, sdr->ch[i]->base + RCAR_DRIF_SICTR);
+ }
+
+ /* Check receive enabled */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR,
+ ctr, ctr & RCAR_DRIF_SICTR_RX_EN,
+ 2, 500000);
+ if (ret) {
+ rdrif_err(sdr, "ch%u: rx en failed. ctr 0x%08x\n",
+ i, readl(sdr->ch[i]->base + RCAR_DRIF_SICTR));
+ break;
+ }
+ }
+ return ret;
+}
+
+/* Disable reception */
+static void rcar_drif_disable_rx(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+ u32 ctr;
+ int ret;
+
+ /* Disable receive */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ctr = readl(sdr->ch[i]->base + RCAR_DRIF_SICTR);
+ ctr &= ~RCAR_DRIF_SICTR_RX_EN;
+ writel(ctr, sdr->ch[i]->base + RCAR_DRIF_SICTR);
+ }
+
+ /* Check receive disabled */
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ret = readl_poll_timeout(sdr->ch[i]->base + RCAR_DRIF_SICTR,
+ ctr, !(ctr & RCAR_DRIF_SICTR_RX_EN),
+ 2, 500000);
+ if (ret)
+ dev_warn(&sdr->vdev->dev,
+ "ch%u: failed to disable rx. ctr 0x%08x\n",
+ i, readl(sdr->ch[i]->base + RCAR_DRIF_SICTR));
+ }
+}
+
+/* Start channel */
+static int rcar_drif_start_channel(struct rcar_drif *ch)
+{
+ struct rcar_drif_sdr *sdr = ch->sdr;
+ u32 ctr, str;
+ int ret;
+
+ /* Reset receive */
+ writel(RCAR_DRIF_SICTR_RESET, ch->base + RCAR_DRIF_SICTR);
+ ret = readl_poll_timeout(ch->base + RCAR_DRIF_SICTR,
+ ctr, !(ctr & RCAR_DRIF_SICTR_RESET),
+ 2, 500000);
+ if (ret) {
+ rdrif_err(sdr, "ch%u: failed to reset rx. ctr 0x%08x\n",
+ ch->num, readl(ch->base + RCAR_DRIF_SICTR));
+ return ret;
+ }
+
+ /* Queue buffers for DMA */
+ ret = rcar_drif_qbuf(ch);
+ if (ret)
+ return ret;
+
+ /* Clear status register flags */
+ str = RCAR_DRIF_RFFUL | RCAR_DRIF_REOF | RCAR_DRIF_RFSERR |
+ RCAR_DRIF_RFUDF | RCAR_DRIF_RFOVF;
+ writel(str, ch->base + RCAR_DRIF_SISTR);
+
+ /* Enable DMA receive interrupt */
+ writel(0x00009000, ch->base + RCAR_DRIF_SIIER);
+
+ return ret;
+}
+
+/* Start receive operation */
+static int rcar_drif_start(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+ int ret;
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ret = rcar_drif_start_channel(sdr->ch[i]);
+ if (ret)
+ goto start_error;
+ }
+
+ sdr->produced = 0;
+ ret = rcar_drif_enable_rx(sdr);
+start_error:
+ return ret;
+}
+
+/* Stop channel */
+static void rcar_drif_stop_channel(struct rcar_drif *ch)
+{
+ struct rcar_drif_sdr *sdr = ch->sdr;
+ int ret, retries = 3;
+
+ /* Disable DMA receive interrupt */
+ writel(0x00000000, ch->base + RCAR_DRIF_SIIER);
+
+ do {
+ /* Terminate all DMA transfers */
+ ret = dmaengine_terminate_sync(ch->dmach);
+ if (!ret)
+ break;
+ rdrif_dbg(2, sdr, "stop retry\n");
+ } while (--retries);
+
+ WARN_ON(!retries);
+}
+
+/* Stop receive operation */
+static void rcar_drif_stop(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ /* Disable Rx */
+ rcar_drif_disable_rx(sdr);
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+ rcar_drif_stop_channel(sdr->ch[i]);
+}
+
+/* Start streaming */
+static int rcar_drif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+ unsigned int i, j;
+ int ret;
+
+ mutex_lock(&sdr->v4l2_mutex);
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) {
+ ret = clk_prepare_enable(sdr->ch[i]->clkp);
+ if (ret)
+ goto start_error;
+ }
+
+ /* Set default MDRx settings */
+ rcar_drif_set_mdr1(sdr);
+
+ /* Set new format */
+ ret = rcar_drif_set_format(sdr);
+ if (ret)
+ goto start_error;
+
+ if (sdr->num_cur_ch == RCAR_DRIF_MAX_CHANNEL)
+ sdr->hwbuf_size =
+ formats[sdr->fmt_idx].buffersize / RCAR_DRIF_MAX_CHANNEL;
+ else
+ sdr->hwbuf_size = formats[sdr->fmt_idx].buffersize;
+
+ rdrif_dbg(1, sdr, "num_hwbufs %u, hwbuf_size %u\n",
+ num_hwbufs, sdr->hwbuf_size);
+
+ /* Alloc DMA channel */
+ ret = rcar_drif_alloc_dmachannel(sdr);
+ if (ret)
+ goto start_error;
+
+ /* Alloc buf context */
+ ret = rcar_drif_alloc_bufctxt(sdr);
+ if (ret)
+ goto start_error;
+
+ /* Request buffers */
+ ret = rcar_drif_request_buf(sdr);
+ if (ret)
+ goto start_error;
+
+ /* Start Rx */
+ ret = rcar_drif_start(sdr);
+ if (ret)
+ goto start_error;
+
+ mutex_unlock(&sdr->v4l2_mutex);
+ rdrif_dbg(1, sdr, "started\n");
+ return ret;
+
+start_error:
+ rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_QUEUED);
+ rcar_drif_release_buf(sdr);
+ rcar_drif_release_bufctxt(sdr);
+ rcar_drif_release_dmachannel(sdr);
+ for (j = 0; j < i; j++)
+ clk_disable_unprepare(sdr->ch[j]->clkp);
+
+ mutex_unlock(&sdr->v4l2_mutex);
+ return ret;
+}
+
+/* Stop streaming */
+static void rcar_drif_stop_streaming(struct vb2_queue *vq)
+{
+ struct rcar_drif_sdr *sdr = vb2_get_drv_priv(vq);
+ unsigned int i;
+
+ mutex_lock(&sdr->v4l2_mutex);
+
+ /* Stop hardware streaming */
+ rcar_drif_stop(sdr);
+
+ /* Return all queued buffers to vb2 */
+ rcar_drif_release_queued_bufs(sdr, VB2_BUF_STATE_ERROR);
+
+ /* Release buf & buf context */
+ rcar_drif_release_buf(sdr);
+ rcar_drif_release_bufctxt(sdr);
+
+ /* Release DMA channel resources */
+ rcar_drif_release_dmachannel(sdr);
+
+ for_each_rcar_drif_channel(i, &sdr->cur_ch_mask)
+ clk_disable_unprepare(sdr->ch[i]->clkp);
+
+ mutex_unlock(&sdr->v4l2_mutex);
+ rdrif_dbg(1, sdr, "stopped: prod %u\n", sdr->produced);
+}
+
+/* Vb2 ops */
+static struct vb2_ops rcar_drif_vb2_ops = {
+ .queue_setup = rcar_drif_queue_setup,
+ .buf_queue = rcar_drif_buf_queue,
+ .start_streaming = rcar_drif_start_streaming,
+ .stop_streaming = rcar_drif_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int rcar_drif_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ strlcpy(cap->card, sdr->vdev->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ sdr->vdev->name);
+ return 0;
+}
+
+static int rcar_drif_set_default_format(struct rcar_drif_sdr *sdr)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ /* Matching fmt based on required channels is set as default */
+ if (sdr->num_hw_ch == formats[i].num_ch) {
+ sdr->fmt_idx = i;
+ sdr->cur_ch_mask = sdr->hw_ch_mask;
+ sdr->num_cur_ch = sdr->num_hw_ch;
+ dev_dbg(sdr->dev, "default fmt[%u]: mask %lu num %u\n",
+ i, sdr->cur_ch_mask, sdr->num_cur_ch);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int rcar_drif_enum_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].pixelformat;
+ return 0;
+}
+
+static int rcar_drif_g_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+
+ f->fmt.sdr.pixelformat = formats[sdr->fmt_idx].pixelformat;
+ f->fmt.sdr.buffersize = formats[sdr->fmt_idx].buffersize;
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ return 0;
+}
+
+static int rcar_drif_s_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+ struct vb2_queue *q = &sdr->vb_queue;
+ unsigned int i;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+ sdr->fmt_idx = i;
+ f->fmt.sdr.buffersize = formats[i].buffersize;
+
+ /*
+ * If a format demands one channel only out of two
+ * enabled channels, pick the 0th channel.
+ */
+ if (formats[i].num_ch < sdr->num_hw_ch) {
+ sdr->cur_ch_mask = BIT(0);
+ sdr->num_cur_ch = formats[i].num_ch;
+ } else {
+ sdr->cur_ch_mask = sdr->hw_ch_mask;
+ sdr->num_cur_ch = sdr->num_hw_ch;
+ }
+
+ rdrif_dbg(1, sdr, "cur: idx %u mask %lu num %u\n",
+ i, sdr->cur_ch_mask, sdr->num_cur_ch);
+ return 0;
+ }
+ }
+
+ if (rcar_drif_set_default_format(sdr)) {
+ rdrif_err(sdr, "cannot set default format\n");
+ return -EINVAL;
+ }
+
+ f->fmt.sdr.pixelformat = formats[sdr->fmt_idx].pixelformat;
+ f->fmt.sdr.buffersize = formats[sdr->fmt_idx].buffersize;
+ return 0;
+}
+
+static int rcar_drif_try_fmt_sdr_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+ unsigned int i;
+
+ memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved));
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+ f->fmt.sdr.buffersize = formats[i].buffersize;
+ return 0;
+ }
+ }
+
+ f->fmt.sdr.pixelformat = formats[sdr->fmt_idx].pixelformat;
+ f->fmt.sdr.buffersize = formats[sdr->fmt_idx].buffersize;
+ return 0;
+}
+
+/* Tuner subdev ioctls */
+static int rcar_drif_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+ struct v4l2_subdev *sd;
+ int ret = 0;
+
+ v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+ ret = v4l2_subdev_call(sd, tuner, enum_freq_bands, band);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+static int rcar_drif_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+ struct v4l2_subdev *sd;
+ int ret = 0;
+
+ v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+ ret = v4l2_subdev_call(sd, tuner, g_frequency, f);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+static int rcar_drif_s_frequency(struct file *file, void *priv,
+ const struct v4l2_frequency *f)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+ struct v4l2_subdev *sd;
+ int ret = 0;
+
+ v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+ ret = v4l2_subdev_call(sd, tuner, s_frequency, f);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+static int rcar_drif_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *vt)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+ struct v4l2_subdev *sd;
+ int ret = 0;
+
+ v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+ ret = v4l2_subdev_call(sd, tuner, g_tuner, vt);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+static int rcar_drif_s_tuner(struct file *file, void *priv,
+ const struct v4l2_tuner *vt)
+{
+ struct rcar_drif_sdr *sdr = video_drvdata(file);
+ struct v4l2_subdev *sd;
+ int ret = 0;
+
+ v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+ ret = v4l2_subdev_call(sd, tuner, s_tuner, vt);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops rcar_drif_ioctl_ops = {
+ .vidioc_querycap = rcar_drif_querycap,
+
+ .vidioc_enum_fmt_sdr_cap = rcar_drif_enum_fmt_sdr_cap,
+ .vidioc_g_fmt_sdr_cap = rcar_drif_g_fmt_sdr_cap,
+ .vidioc_s_fmt_sdr_cap = rcar_drif_s_fmt_sdr_cap,
+ .vidioc_try_fmt_sdr_cap = rcar_drif_try_fmt_sdr_cap,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_s_frequency = rcar_drif_s_frequency,
+ .vidioc_g_frequency = rcar_drif_g_frequency,
+ .vidioc_s_tuner = rcar_drif_s_tuner,
+ .vidioc_g_tuner = rcar_drif_g_tuner,
+ .vidioc_enum_freq_bands = rcar_drif_enum_freq_bands,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations rcar_drif_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rcar_drif_sdr *sdr =
+ container_of(notifier, struct rcar_drif_sdr, notifier);
+
+ /* Nothing to do at this point */
+ rdrif_dbg(2, sdr, "bound asd: %s\n", asd->match.of.node->name);
+ return 0;
+}
+
+/* Sub-device registered notification callback */
+static int rcar_drif_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rcar_drif_sdr *sdr =
+ container_of(notifier, struct rcar_drif_sdr, notifier);
+ struct v4l2_subdev *sd;
+ int ret;
+
+ sdr->v4l2_dev.ctrl_handler = &sdr->ctrl_hdl;
+
+ ret = v4l2_device_register_subdev_nodes(&sdr->v4l2_dev);
+ if (ret) {
+ rdrif_err(sdr, "failed register subdev nodes ret %d\n", ret);
+ return ret;
+ }
+
+ v4l2_device_for_each_subdev(sd, &sdr->v4l2_dev) {
+ ret = v4l2_ctrl_add_handler(sdr->v4l2_dev.ctrl_handler,
+ sd->ctrl_handler, NULL);
+ if (ret) {
+ rdrif_err(sdr, "failed ctrl add hdlr ret %d\n", ret);
+ return ret;
+ }
+ }
+ rdrif_dbg(2, sdr, "notify complete\n");
+ return 0;
+}
+
+/* Parse sub-devs (tuner) to find a matching device */
+static int rcar_drif_parse_subdevs(struct device *dev,
+ struct v4l2_async_notifier *notifier)
+{
+ struct device_node *node;
+ struct rcar_drif_async_subdev *rsd;
+
+ notifier->subdevs = devm_kzalloc(dev, sizeof(*notifier->subdevs),
+ GFP_KERNEL);
+ if (!notifier->subdevs)
+ return -ENOMEM;
+
+ node = of_graph_get_next_endpoint(dev->of_node, NULL);
+ if (!node)
+ return 0;
+
+
+ rsd = devm_kzalloc(dev, sizeof(*rsd), GFP_KERNEL);
+ if (!rsd) {
+ of_node_put(node);
+ return -ENOMEM;
+ }
+
+ notifier->subdevs[notifier->num_subdevs] = &rsd->asd;
+ rsd->asd.match.of.node = of_graph_get_remote_port_parent(node);
+ of_node_put(node);
+ if (!rsd->asd.match.of.node) {
+ dev_warn(dev, "bad remote port parent\n");
+ return -EINVAL;
+ }
+
+ rsd->asd.match_type = V4L2_ASYNC_MATCH_OF;
+ notifier->num_subdevs++;
+ return 0;
+}
+
+/* SIRMDR1 configuration */
+static int rcar_drif_validate_syncmd(struct rcar_drif_sdr *sdr, u32 val)
+{
+ if (val > 1) {
+ dev_err(sdr->dev, "invalid syncmd %u\n", val);
+ return -EINVAL;
+ }
+
+ sdr->mdr1 &= ~(3 << 28); /* Clear current settings */
+ if (val == 0)
+ sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_FRAME;
+ else
+ sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCMD_LR;
+ return 0;
+}
+
+/* Get the dtdl or syncdl bits */
+static u32 rcar_drif_get_dtdl_or_syncdl_bits(u32 dtdl_or_syncdl)
+{
+ /*
+ * DTDL/SYNCDL bit : dtdl/syncdl
+ * b'000 : 0
+ * b'001 : 2
+ * b'010 : 4
+ * b'011 (SYNCDL only) : 6
+ * b'101 : 1
+ * b'110 : 3
+ */
+ if (dtdl_or_syncdl % 2)
+ return dtdl_or_syncdl / 2 + 5;
+
+ return dtdl_or_syncdl / 2;
+}
+
+/* Validate DT properties dtdl and syncdl */
+static int rcar_drif_validate_dtdl_syncdl(struct rcar_drif_sdr *sdr)
+{
+ struct device_node *np = sdr->dev->of_node;
+ u32 dtdl = 2, syncdl = 0; /* delay in 0.5 clock cycle units */
+
+ sdr->mdr1 |= RCAR_DRIF_SIRMDR1_DTDL_1 | RCAR_DRIF_SIRMDR1_SYNCDL_0;
+ of_property_read_u32(np, "renesas,dtdl", &dtdl);
+ of_property_read_u32(np, "renesas,syncdl", &syncdl);
+
+ /* Sanity checks */
+ if (dtdl > 4 || syncdl > 6) {
+ dev_err(sdr->dev, "invalid dtdl %u/syncdl %u\n", dtdl, syncdl);
+ return -EINVAL;
+ }
+ if ((dtdl + syncdl) % 2) {
+ dev_err(sdr->dev, "sum of dtdl %u & syncdl %u not OK\n",
+ dtdl, syncdl);
+ return -EINVAL;
+ }
+ sdr->mdr1 &= ~(7 << 20) & ~(7 << 16); /* Clear current settings */
+ sdr->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(dtdl) << 20;
+ sdr->mdr1 |= rcar_drif_get_dtdl_or_syncdl_bits(syncdl) << 16;
+ return 0;
+}
+
+static int rcar_drif_parse_properties(struct rcar_drif_sdr *sdr)
+{
+ struct device_node *np = sdr->dev->of_node;
+ u32 val;
+ int ret;
+
+ /* Set the defaults and check for overrides */
+ sdr->mdr1 = RCAR_DRIF_SIRMDR1_SYNCMD_LR;
+ if (!of_property_read_u32(np, "renesas,syncmd", &val)) {
+ ret = rcar_drif_validate_syncmd(sdr, val);
+ if (ret)
+ return ret;
+ }
+
+ if (of_find_property(np, "renesas,lsb-first", NULL))
+ sdr->mdr1 |= RCAR_DRIF_SIRMDR1_LSB_FIRST;
+ else
+ sdr->mdr1 |= RCAR_DRIF_SIRMDR1_MSB_FIRST;
+
+ sdr->mdr1 |= RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH;
+ if (!of_property_read_u32(np, "renesas,syncac-active", &val))
+ sdr->mdr1 |= val ? RCAR_DRIF_SIRMDR1_SYNCAC_POL_HIGH :
+ RCAR_DRIF_SIRMDR1_SYNCAC_POL_LOW;
+
+ return rcar_drif_validate_dtdl_syncdl(sdr);
+}
+
+static bool rcar_drif_primary_bond(struct platform_device *pdev)
+{
+ if (of_find_property(pdev->dev.of_node, "renesas,primary-bond", NULL))
+ return true;
+
+ return false;
+}
+
+static struct platform_device *rcar_drif_enabled_bond(struct platform_device *p)
+{
+ struct device_node *np;
+
+ np = of_parse_phandle(p->dev.of_node, "renesas,bonding", 0);
+ if (np && of_device_is_available(np))
+ return of_find_device_by_node(np);
+
+ return NULL;
+}
+
+static int rcar_drif_channel_probe(struct platform_device *pdev)
+{
+ struct rcar_drif *ch;
+ struct resource *res;
+ void __iomem *base;
+ struct clk *clkp;
+ int ret;
+
+ /* Peripheral clock */
+ clkp = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clkp)) {
+ ret = PTR_ERR(clkp);
+ dev_err(&pdev->dev, "clk get failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* Register map */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ dev_err(&pdev->dev, "ioremap failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* Reserve memory for enabled channel */
+ ch = devm_kzalloc(&pdev->dev, sizeof(*ch), GFP_KERNEL);
+ if (!ch) {
+ ret = PTR_ERR(ch);
+ dev_err(&pdev->dev, "failed alloc channel\n");
+ return ret;
+ }
+ ch->pdev = pdev;
+ ch->clkp = clkp;
+ ch->base = base;
+ ch->start = res->start;
+ platform_set_drvdata(pdev, ch);
+ return 0;
+}
+
+static int rcar_drif_probe(struct platform_device *pdev)
+{
+ struct rcar_drif *ch, *b_ch = NULL;
+ struct platform_device *b_pdev;
+ struct rcar_drif_sdr *sdr;
+ int ret;
+
+ /* Probe internal channel */
+ ret = rcar_drif_channel_probe(pdev);
+ if (ret)
+ return ret;
+
+ /* Check if both channels of the bond are enabled */
+ b_pdev = rcar_drif_enabled_bond(pdev);
+ if (b_pdev) {
+ /* Check if current channel acting as primary-bond */
+ if (!rcar_drif_primary_bond(pdev)) {
+ dev_notice(&pdev->dev, "probed\n");
+ return 0;
+ }
+
+ /* Check if the other device is probed */
+ b_ch = platform_get_drvdata(b_pdev);
+ if (!b_ch) {
+ dev_info(&pdev->dev, "defer probe\n");
+ return -EPROBE_DEFER;
+ }
+ /* Set the other channel number */
+ b_ch->num = 1;
+ }
+
+ /* Channel acting as SDR instance */
+ ch = platform_get_drvdata(pdev);
+ ch->acting_sdr = true;
+
+ /* Reserve memory for SDR structure */
+ sdr = devm_kzalloc(&pdev->dev, sizeof(*sdr), GFP_KERNEL);
+ if (!sdr) {
+ ret = PTR_ERR(sdr);
+ dev_err(&pdev->dev, "failed alloc drif context\n");
+ return ret;
+ }
+ sdr->dev = &pdev->dev;
+ sdr->hw_ch_mask = BIT(ch->num);
+
+ /* Establish links between SDR and channel(s) */
+ ch->sdr = sdr;
+ sdr->ch[ch->num] = ch;
+ if (b_ch) {
+ sdr->ch[b_ch->num] = b_ch;
+ b_ch->sdr = sdr;
+ sdr->hw_ch_mask |= BIT(b_ch->num);
+ }
+ sdr->num_hw_ch = hweight_long(sdr->hw_ch_mask);
+
+ /* Parse device tree properties */
+ ret = rcar_drif_parse_properties(sdr);
+ if (ret)
+ return ret;
+
+ dev_dbg(sdr->dev, "parsed mdr1 0x%08x\n", sdr->mdr1);
+
+ /* Validate any supported format for enabled channels */
+ ret = rcar_drif_set_default_format(sdr);
+ if (ret) {
+ dev_err(sdr->dev, "failed to set default format\n");
+ return ret;
+ }
+
+ /* Set defaults */
+ sdr->hwbuf_size = RCAR_DRIF_DEFAULT_HWBUF_SIZE;
+
+ mutex_init(&sdr->v4l2_mutex);
+ mutex_init(&sdr->vb_queue_mutex);
+ spin_lock_init(&sdr->queued_bufs_lock);
+ INIT_LIST_HEAD(&sdr->queued_bufs);
+
+ /* Init videobuf2 queue structure */
+ sdr->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+ sdr->vb_queue.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
+ sdr->vb_queue.drv_priv = sdr;
+ sdr->vb_queue.buf_struct_size = sizeof(struct rcar_drif_frame_buf);
+ sdr->vb_queue.ops = &rcar_drif_vb2_ops;
+ sdr->vb_queue.mem_ops = &vb2_vmalloc_memops;
+ sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ /* Init videobuf2 queue */
+ ret = vb2_queue_init(&sdr->vb_queue);
+ if (ret) {
+ dev_err(sdr->dev, "could not initialize vb2 queue\n");
+ return ret;
+ }
+
+ /* Register the v4l2_device */
+ ret = v4l2_device_register(&pdev->dev, &sdr->v4l2_dev);
+ if (ret) {
+ dev_err(sdr->dev, "failed v4l2_device_register (%d)\n", ret);
+ return ret;
+ }
+
+ /*
+ * Parse subdevs after v4l2_device_register because if the subdev
+ * is already probed, bound and complete will be called immediately
+ */
+ ret = rcar_drif_parse_subdevs(&pdev->dev, &sdr->notifier);
+ if (ret)
+ goto err_unreg_v4l2;
+
+ sdr->notifier.bound = rcar_drif_notify_bound;
+ sdr->notifier.complete = rcar_drif_notify_complete;
+
+ v4l2_ctrl_handler_init(&sdr->ctrl_hdl, 10);
+
+ /* Register notifier */
+ ret = v4l2_async_notifier_register(&sdr->v4l2_dev, &sdr->notifier);
+ if (ret < 0) {
+ dev_err(sdr->dev, "notifier registration failed (%d)\n", ret);
+ goto err_unreg_v4l2;
+ }
+
+ /* Init video_device structure */
+ sdr->vdev = video_device_alloc();
+ if (!sdr->vdev) {
+ ret = -ENOMEM;
+ goto err_unreg_notif;
+ }
+ snprintf(sdr->vdev->name, sizeof(sdr->vdev->name), "R-Car DRIF");
+ sdr->vdev->fops = &rcar_drif_fops;
+ sdr->vdev->ioctl_ops = &rcar_drif_ioctl_ops;
+ sdr->vdev->release = video_device_release;
+ sdr->vdev->lock = &sdr->v4l2_mutex;
+ sdr->vdev->queue = &sdr->vb_queue;
+ sdr->vdev->queue->lock = &sdr->vb_queue_mutex;
+ sdr->vdev->ctrl_handler = &sdr->ctrl_hdl;
+ sdr->vdev->v4l2_dev = &sdr->v4l2_dev;
+ sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+ video_set_drvdata(sdr->vdev, sdr);
+
+ /* Register V4L2 SDR device */
+ ret = video_register_device(sdr->vdev, VFL_TYPE_SDR, -1);
+ if (ret) {
+ dev_err(sdr->dev, "failed video_register_device (%d)\n", ret);
+ goto err_unreg_notif;
+ }
+
+ dev_notice(sdr->dev, "probed\n");
+ return 0;
+
+err_unreg_notif:
+ video_device_release(sdr->vdev);
+ v4l2_async_notifier_unregister(&sdr->notifier);
+err_unreg_v4l2:
+ v4l2_device_unregister(&sdr->v4l2_dev);
+ return ret;
+}
+
+static int rcar_drif_remove(struct platform_device *pdev)
+{
+ struct rcar_drif *ch = platform_get_drvdata(pdev);
+ struct rcar_drif_sdr *sdr = ch->sdr;
+
+ if (!ch->acting_sdr) {
+ /* Nothing to do */
+ dev_notice(&pdev->dev, "removed\n");
+ return 0;
+ }
+
+ /* SDR instance */
+ v4l2_ctrl_handler_free(sdr->v4l2_dev.ctrl_handler);
+ v4l2_async_notifier_unregister(&sdr->notifier);
+ v4l2_device_unregister(&sdr->v4l2_dev);
+ video_unregister_device(sdr->vdev);
+ dev_notice(&pdev->dev, "removed\n");
+ return 0;
+}
+
+static int __maybe_unused rcar_drif_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int __maybe_unused rcar_drif_resume(struct device *dev)
+{
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rcar_drif_pm_ops, rcar_drif_suspend,
+ rcar_drif_resume);
+
+static const struct of_device_id rcar_drif_of_table[] = {
+ { .compatible = "renesas,rcar-gen3-drif" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rcar_drif_of_table);
+
+#define RCAR_DRIF_DRV_NAME "rcar_drif"
+static struct platform_driver rcar_drif_driver = {
+ .driver = {
+ .name = RCAR_DRIF_DRV_NAME,
+ .of_match_table = of_match_ptr(rcar_drif_of_table),
+ .pm = &rcar_drif_pm_ops,
+ },
+ .probe = rcar_drif_probe,
+ .remove = rcar_drif_remove,
+};
+
+module_platform_driver(rcar_drif_driver);
+
+MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver");
+MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>");
--
1.9.1
^ permalink raw reply related
* [PATCH v2 6/7] dt-bindings: media: Add Renesas R-Car DRIF binding
From: Ramesh Shanmugasundaram @ 2016-12-21 8:10 UTC (permalink / raw)
To: robh+dt, mark.rutland, mchehab, hverkuil, sakari.ailus, crope
Cc: chris.paterson2, laurent.pinchart, geert+renesas, linux-media,
devicetree, linux-renesas-soc, Ramesh Shanmugasundaram
In-Reply-To: <1482307838-47415-1-git-send-email-ramesh.shanmugasundaram@bp.renesas.com>
Add binding documentation for Renesas R-Car Digital Radio Interface
(DRIF) controller.
Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
---
.../devicetree/bindings/media/renesas,drif.txt | 202 +++++++++++++++++++++
1 file changed, 202 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/renesas,drif.txt
diff --git a/Documentation/devicetree/bindings/media/renesas,drif.txt b/Documentation/devicetree/bindings/media/renesas,drif.txt
new file mode 100644
index 0000000..1f3feaf
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/renesas,drif.txt
@@ -0,0 +1,202 @@
+Renesas R-Car Gen3 Digital Radio Interface controller (DRIF)
+------------------------------------------------------------
+
+R-Car Gen3 DRIF is a SPI like receive only slave device. A general
+representation of DRIF interfacing with a master device is shown below.
+
++---------------------+ +---------------------+
+| |-----SCK------->|CLK |
+| Master |-----SS-------->|SYNC DRIFn (slave) |
+| |-----SD0------->|D0 |
+| |-----SD1------->|D1 |
++---------------------+ +---------------------+
+
+As per datasheet, each DRIF channel (drifn) is made up of two internal
+channels (drifn0 & drifn1). These two internal channels share the common
+CLK & SYNC. Each internal channel has its own dedicated resources like
+irq, dma channels, address space & clock. This internal split is not
+visible to the external master device.
+
+The device tree model represents each internal channel as a separate node.
+The internal channels sharing the CLK & SYNC are tied together by their
+phandles using a new property called "renesas,bonding". For the rest of
+the documentation, unless explicitly stated, the word channel implies an
+internal channel.
+
+When both internal channels are enabled they need to be managed together
+as one (i.e.) they cannot operate alone as independent devices. Out of the
+two, one of them needs to act as a primary device that accepts common
+properties of both the internal channels. This channel is identified by a
+new property called "renesas,primary-bond".
+
+To summarize,
+ - When both the internal channels that are bonded together are enabled,
+ the zeroth channel is selected as primary-bond. This channels accepts
+ properties common to all the members of the bond.
+ - When only one of the bonded channels need to be enabled, the property
+ "renesas,bonding" or "renesas,primary-bond" will have no effect. That
+ enabled channel can act alone as any other independent device.
+
+Required properties of an internal channel:
+-------------------------------------------
+- compatible: "renesas,r8a7795-drif" if DRIF controller is a part of R8A7795 SoC.
+ "renesas,rcar-gen3-drif" for a generic R-Car Gen3 compatible device.
+ When compatible with the generic version, nodes must list the
+ SoC-specific version corresponding to the platform first
+ followed by the generic version.
+- reg: offset and length of that channel.
+- interrupts: associated with that channel.
+- clocks: phandle and clock specifier of that channel.
+- clock-names: clock input name string: "fck".
+- dmas: phandles to the DMA channels.
+- dma-names: names of the DMA channel: "rx".
+- renesas,bonding: phandle to the other channel.
+
+Optional properties of an internal channel:
+-------------------------------------------
+- power-domains: phandle to the respective power domain.
+
+Required properties of an internal channel when:
+ - It is the only enabled channel of the bond (or)
+ - If it acts as primary among enabled bonds
+--------------------------------------------------------
+- pinctrl-0: pin control group to be used for this channel.
+- pinctrl-names: must be "default".
+- renesas,primary-bond: empty property indicating the channel acts as primary
+ among the bonded channels.
+- port: child port node of a channel that defines the local and remote
+ endpoints. The remote endpoint is assumed to be a third party tuner
+ device endpoint.
+
+Optional properties of an internal channel when:
+ - It is the only enabled channel of the bond (or)
+ - If it acts as primary among enabled bonds
+--------------------------------------------------------
+- renesas,syncmd : sync mode
+ 0 (Frame start sync pulse mode. 1-bit width pulse
+ indicates start of a frame)
+ 1 (L/R sync or I2S mode) (default)
+- renesas,lsb-first : empty property indicates lsb bit is received first.
+ When not defined msb bit is received first (default)
+- renesas,syncac-active: Indicates sync signal polarity, 0/1 for low/high
+ respectively. The default is 1 (active high)
+- renesas,dtdl : delay between sync signal and start of reception.
+ The possible values are represented in 0.5 clock
+ cycle units and the range is 0 to 4. The default
+ value is 2 (i.e.) 1 clock cycle delay.
+- renesas,syncdl : delay between end of reception and sync signal edge.
+ The possible values are represented in 0.5 clock
+ cycle units and the range is 0 to 4 & 6. The default
+ value is 0 (i.e.) no delay.
+
+Example
+--------
+
+SoC common dtsi file
+
+ drif00: rif@e6f40000 {
+ compatible = "renesas,r8a7795-drif",
+ "renesas,rcar-gen3-drif";
+ reg = <0 0xe6f40000 0 0x64>;
+ interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cpg CPG_MOD 515>;
+ clock-names = "fck";
+ dmas = <&dmac1 0x20>, <&dmac2 0x20>;
+ dma-names = "rx", "rx";
+ power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ renesas,bonding = <&drif01>;
+ status = "disabled";
+ };
+
+ drif01: rif@e6f50000 {
+ compatible = "renesas,r8a7795-drif",
+ "renesas,rcar-gen3-drif";
+ reg = <0 0xe6f50000 0 0x64>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cpg CPG_MOD 514>;
+ clock-names = "fck";
+ dmas = <&dmac1 0x22>, <&dmac2 0x22>;
+ dma-names = "rx", "rx";
+ power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
+ renesas,bonding = <&drif00>;
+ status = "disabled";
+ };
+
+
+Board specific dts file
+
+(1) Both internal channels enabled, primary-bond = 0
+-----------------------------------------------------
+
+When interfacing with a third party tuner device with two data pins as shown
+below.
+
++---------------------+ +---------------------+
+| |-----SCK------->|CLK |
+| Master |-----SS-------->|SYNC DRIFn (slave) |
+| |-----SD0------->|D0 |
+| |-----SD1------->|D1 |
++---------------------+ +---------------------+
+
+pfc {
+ ...
+
+ drif0_pins: drif0 {
+ groups = "drif0_ctrl_a", "drif0_data0_a",
+ "drif0_data1_a";
+ function = "drif0";
+ };
+ ...
+}
+
+&drif00 {
+ pinctrl-0 = <&drif0_pins>;
+ pinctrl-names = "default";
+ renesas,syncac-active = <1>;
+ renesas,primary-bond;
+ status = "okay";
+ port {
+ drif0_ep: endpoint {
+ remote-endpoint = <&tuner_ep>;
+ };
+ };
+};
+
+&drif01 {
+ status = "okay";
+};
+
+(2) Internal channel 1 alone is enabled:
+----------------------------------------
+
+When interfacing with a third party tuner device with one data pin as shown
+below.
+
++---------------------+ +---------------------+
+| |-----SCK------->|CLK |
+| Master |-----SS-------->|SYNC DRIFn (slave) |
+| | |D0 (unused) |
+| |-----SD-------->|D1 |
++---------------------+ +---------------------+
+
+pfc {
+ ...
+
+ drif0_pins: drif0 {
+ groups = "drif0_ctrl_a", "drif0_data1_a";
+ function = "drif0";
+ };
+ ...
+}
+
+&drif01 {
+ pinctrl-0 = <&drif0_pins>;
+ pinctrl-names = "default";
+ renesas,syncac-active = <0>;
+ status = "okay";
+ port {
+ drif0_ep: endpoint {
+ remote-endpoint = <&tuner_ep>;
+ };
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v2 5/7] doc_rst: media: New SDR formats PC16, PC18 & PC20
From: Ramesh Shanmugasundaram @ 2016-12-21 8:10 UTC (permalink / raw)
To: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA, crope-X3B1VOXEql0
Cc: chris.paterson2-zM6kxYcvzFBBDgjK7y7TUQ,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA, Ramesh Shanmugasundaram
In-Reply-To: <1482307838-47415-1-git-send-email-ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
This patch adds documentation for the three new SDR formats
V4L2_SDR_FMT_PCU16BE
V4L2_SDR_FMT_PCU18BE
V4L2_SDR_FMT_PCU20BE
Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram-kTT6dE0pTRh9uiUsa/gSgQ@public.gmane.org>
---
.../media/uapi/v4l/pixfmt-sdr-pcu16be.rst | 55 ++++++++++++++++++++++
.../media/uapi/v4l/pixfmt-sdr-pcu18be.rst | 55 ++++++++++++++++++++++
.../media/uapi/v4l/pixfmt-sdr-pcu20be.rst | 55 ++++++++++++++++++++++
Documentation/media/uapi/v4l/sdr-formats.rst | 3 ++
4 files changed, 168 insertions(+)
create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
create mode 100644 Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
new file mode 100644
index 0000000..2de1b1a
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu16be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-PCU16BE:
+
+******************************
+V4L2_SDR_FMT_PCU16BE ('PC16')
+******************************
+
+Planar complex unsigned 16-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 16 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 16 bits, bit 15:2 (14 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+ :header-rows: 1
+ :stub-columns: 0
+
+ * - Offset:
+ - Byte B0
+ - Byte B1
+ - Byte B2
+ - Byte B3
+ * - start + 0:
+ - I'\ :sub:`0[13:6]`
+ - I'\ :sub:`0[5:0]; B1[1:0]=pad`
+ - pad
+ - pad
+ * - start + 4:
+ - I'\ :sub:`1[13:6]`
+ - I'\ :sub:`1[5:0]; B1[1:0]=pad`
+ - pad
+ - pad
+ * - ...
+ * - start + offset:
+ - Q'\ :sub:`0[13:6]`
+ - Q'\ :sub:`0[5:0]; B1[1:0]=pad`
+ - pad
+ - pad
+ * - start + offset + 4:
+ - Q'\ :sub:`1[13:6]`
+ - Q'\ :sub:`1[5:0]; B1[1:0]=pad`
+ - pad
+ - pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
new file mode 100644
index 0000000..da8b26b
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu18be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+
+.. _V4L2-SDR-FMT-PCU18BE:
+
+******************************
+V4L2_SDR_FMT_PCU18BE ('PC18')
+******************************
+
+Planar complex unsigned 18-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 18 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 18 bits, bit 17:2 (16 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+ :header-rows: 1
+ :stub-columns: 0
+
+ * - Offset:
+ - Byte B0
+ - Byte B1
+ - Byte B2
+ - Byte B3
+ * - start + 0:
+ - I'\ :sub:`0[17:10]`
+ - I'\ :sub:`0[9:2]`
+ - I'\ :sub:`0[1:0]; B2[5:0]=pad`
+ - pad
+ * - start + 4:
+ - I'\ :sub:`1[17:10]`
+ - I'\ :sub:`1[9:2]`
+ - I'\ :sub:`1[1:0]; B2[5:0]=pad`
+ - pad
+ * - ...
+ * - start + offset:
+ - Q'\ :sub:`0[17:10]`
+ - Q'\ :sub:`0[9:2]`
+ - Q'\ :sub:`0[1:0]; B2[5:0]=pad`
+ - pad
+ * - start + offset + 4:
+ - Q'\ :sub:`1[17:10]`
+ - Q'\ :sub:`1[9:2]`
+ - Q'\ :sub:`1[1:0]; B2[5:0]=pad`
+ - pad
diff --git a/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst
new file mode 100644
index 0000000..b073be5
--- /dev/null
+++ b/Documentation/media/uapi/v4l/pixfmt-sdr-pcu20be.rst
@@ -0,0 +1,55 @@
+.. -*- coding: utf-8; mode: rst -*-
+.. _V4L2-SDR-FMT-PCU20BE:
+
+******************************
+V4L2_SDR_FMT_PCU20BE ('PC20')
+******************************
+
+Planar complex unsigned 20-bit big endian IQ sample
+
+Description
+===========
+
+This format contains a sequence of complex number samples. Each complex
+number consist of two parts called In-phase and Quadrature (IQ). Both I
+and Q are represented as a 20 bit unsigned big endian number stored in
+32 bit space. The remaining unused bits within the 32 bit space will be
+padded with 0. I value starts first and Q value starts at an offset
+equalling half of the buffer size (i.e.) offset = buffersize/2. Out of
+the 20 bits, bit 19:2 (18 bit) is data and bit 1:0 (2 bit) can be any
+value.
+
+**Byte Order.**
+Each cell is one byte.
+
+.. flat-table::
+ :header-rows: 1
+ :stub-columns: 0
+
+ * - Offset:
+ - Byte B0
+ - Byte B1
+ - Byte B2
+ - Byte B3
+ * - start + 0:
+ - I'\ :sub:`0[19:12]`
+ - I'\ :sub:`0[11:4]`
+ - I'\ :sub:`0[3:0]; B2[3:0]=pad`
+ - pad
+ * - start + 4:
+ - I'\ :sub:`1[19:12]`
+ - I'\ :sub:`1[11:4]`
+ - I'\ :sub:`1[3:0]; B2[3:0]=pad`
+ - pad
+ * - ...
+ * - start + offset:
+ - Q'\ :sub:`0[19:12]`
+ - Q'\ :sub:`0[11:4]`
+ - Q'\ :sub:`0[3:0]; B2[3:0]=pad`
+ - pad
+ * - start + offset + 4:
+ - Q'\ :sub:`1[19:12]`
+ - Q'\ :sub:`1[11:4]`
+ - Q'\ :sub:`1[3:0]; B2[3:0]=pad`
+ - pad
+
diff --git a/Documentation/media/uapi/v4l/sdr-formats.rst b/Documentation/media/uapi/v4l/sdr-formats.rst
index f863c08..2037f5b 100644
--- a/Documentation/media/uapi/v4l/sdr-formats.rst
+++ b/Documentation/media/uapi/v4l/sdr-formats.rst
@@ -17,3 +17,6 @@ These formats are used for :ref:`SDR <sdr>` interface only.
pixfmt-sdr-cs08
pixfmt-sdr-cs14le
pixfmt-sdr-ru12le
+ pixfmt-sdr-pcu16be
+ pixfmt-sdr-pcu18be
+ pixfmt-sdr-pcu20be
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox