* [PATCHv2 1/2] Input: pwm-vibra: new driver
From: Sebastian Reichel @ 2017-05-03 11:11 UTC (permalink / raw)
To: Sebastian Reichel, Dmitry Torokhov, Tony Lindgren
Cc: Rob Herring, linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-omap-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Sebastian Reichel
In-Reply-To: <20170503111128.15385-1-sebastian.reichel-ZGY8ohtN/8pPYcu2f3hruQ@public.gmane.org>
Provide a simple driver for PWM controllable vibrators. It
will be used by Motorola Droid 4.
Signed-off-by: Sebastian Reichel <sebastian.reichel-ZGY8ohtN/8pPYcu2f3hruQ@public.gmane.org>
---
Changes since PATCHv1:
- move driver removal code to input->close function
- mark PM functions __maybe_unused and drop #ifdef CONFIG_PM_SLEEP
- remove duplicate NULL check for vibrator in probe function
- cancel work in suspend function
---
.../devicetree/bindings/input/pwm-vibrator.txt | 60 ++++
drivers/input/misc/Kconfig | 11 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/pwm-vibra.c | 343 +++++++++++++++++++++
4 files changed, 415 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/pwm-vibrator.txt
create mode 100644 drivers/input/misc/pwm-vibra.c
diff --git a/Documentation/devicetree/bindings/input/pwm-vibrator.txt b/Documentation/devicetree/bindings/input/pwm-vibrator.txt
new file mode 100644
index 000000000000..c35be4691366
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/pwm-vibrator.txt
@@ -0,0 +1,60 @@
+* PWM vibrator device tree bindings
+
+Registers a PWM device as vibrator.
+
+Required properties:
+- compatible: should be
+ * "pwm-vibrator"
+ For vibrators controlled using the PWM channel's duty cycle (higher duty means
+ the vibrator becomes stronger).
+ * "motorola,mapphone-pwm-vibrator"
+ For vibrator found in Motorola Droid 4. This vibrator generates a pulse for
+ every rising edge, so its controlled using a duty cycle of 50% and changing
+ the period time.
+- pwm-names: Should contain "enable" and optionally "direction"
+- pwms: Should contain a PWM handle for each entry in pwm-names
+
+Example from Motorola Droid 4:
+
+&omap4_pmx_core {
+ vibrator_direction_pin: pinmux_vibrator_direction_pin {
+ pinctrl-single,pins = <
+ OMAP4_IOPAD(0x1ce, PIN_OUTPUT | MUX_MODE1) /* dmtimer8_pwm_evt (gpio_27) */
+ >;
+ };
+
+ vibrator_enable_pin: pinmux_vibrator_enable_pin {
+ pinctrl-single,pins = <
+ OMAP4_IOPAD(0X1d0, PIN_OUTPUT | MUX_MODE1) /* dmtimer9_pwm_evt (gpio_28) */
+ >;
+ };
+};
+
+/ {
+ pwm8: dmtimer-pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&vibrator_direction_pin>;
+
+ compatible = "ti,omap-dmtimer-pwm";
+ #pwm-cells = <3>;
+ ti,timers = <&timer8>;
+ ti,clock-source = <0x01>;
+ };
+
+ pwm9: dmtimer-pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&vibrator_enable_pin>;
+
+ compatible = "ti,omap-dmtimer-pwm";
+ #pwm-cells = <3>;
+ ti,timers = <&timer9>;
+ ti,clock-source = <0x01>;
+ };
+
+ vibrator {
+ compatible = "pwm-vibrator";
+ pwms = <&pwm8 0 1000000000 0>,
+ <&pwm9 0 1000000000 0>;
+ pwm-names = "enable", "direction";
+ };
+};
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5b6c52210d20..d8e0b25eb217 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -571,6 +571,17 @@ config INPUT_PWM_BEEPER
To compile this driver as a module, choose M here: the module will be
called pwm-beeper.
+config INPUT_PWM_VIBRA
+ tristate "PWM vibrator support"
+ depends on PWM
+ help
+ Say Y here to get support for PWM based vibrator devices.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pwm-vibra.
+
config INPUT_GPIO_ROTARY_ENCODER
tristate "Rotary encoders connected to GPIO pins"
depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index b10523f2878e..9a6517f5458c 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o
obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
+obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
diff --git a/drivers/input/misc/pwm-vibra.c b/drivers/input/misc/pwm-vibra.c
new file mode 100644
index 000000000000..878a483f93ff
--- /dev/null
+++ b/drivers/input/misc/pwm-vibra.c
@@ -0,0 +1,343 @@
+/*
+ * PWM vibrator driver
+ *
+ * Copyright (C) 2017 Collabora Ltd.
+ *
+ * Based on previous work from:
+ * Copyright (C) 2012 Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * Based on PWM beeper driver:
+ * Copyright (C) 2010, Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#define DEBUG
+
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/**
+ * Motorola Droid 4 (also known as mapphone), has a vibrator, which pulses
+ * 1x on rising edge. Increasing the pwm period results in more pulses per
+ * second, but reduces intensity. There is also a second channel to control
+ * the vibrator's rotation direction to increase effect. The following
+ * numbers were determined manually. Going below 12.5 Hz means, clearly
+ * noticeable pauses and at 30 Hz the vibration is just barely noticable
+ * anymore.
+ */
+#define MAPPHONE_MIN_FREQ 125 /* 12.5 Hz */
+#define MAPPHONE_MAX_FREQ 300 /* 30.0 Hz */
+
+struct pwm_vibrator_hw {
+ void (*setup_pwm)(u16 level, struct pwm_state *);
+ void (*setup_pwm_dir)(u16 level, struct pwm_state *);
+};
+
+struct pwm_vibrator {
+ struct input_dev *input;
+ struct pwm_device *pwm;
+ struct pwm_device *pwm_dir;
+ struct regulator *vcc;
+
+ struct work_struct play_work;
+ u16 level;
+
+ const struct pwm_vibrator_hw *hw;
+};
+
+static void pwm_vibrator_setup_generic(u16 level, struct pwm_state *state)
+{
+ /* period is configured by platform, duty cycle controls strength */
+ pwm_set_relative_duty_cycle(state, level, 0xffff);
+}
+
+static void pwm_vibrator_setup_dir_generic(u16 level, struct pwm_state *state)
+{
+ /* period is configured by platform, duty cycle controls strength */
+ pwm_set_relative_duty_cycle(state, 50, 100);
+}
+
+static struct pwm_vibrator_hw pwm_vib_hw_generic = {
+ .setup_pwm = pwm_vibrator_setup_generic,
+ .setup_pwm_dir = pwm_vibrator_setup_dir_generic,
+};
+
+static void pwm_vibrator_setup_mapphone(u16 level, struct pwm_state *state)
+{
+ unsigned int freq;
+
+ /* convert [0, 0xffff] -> [MAPPHONE_MAX_FREQ, MAPPHONE_MIN_FREQ] */
+ freq = 0xffff - level;
+ freq *= MAPPHONE_MAX_FREQ - MAPPHONE_MIN_FREQ;
+ freq /= 0xffff;
+ freq += MAPPHONE_MIN_FREQ;
+
+ state->period = DIV_ROUND_CLOSEST_ULL((u64) NSEC_PER_SEC * 10, freq);
+ pwm_set_relative_duty_cycle(state, 50, 100);
+}
+
+static struct pwm_vibrator_hw pwm_vib_hw_mapphone = {
+ .setup_pwm = pwm_vibrator_setup_mapphone,
+ .setup_pwm_dir = pwm_vibrator_setup_mapphone,
+};
+
+static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
+{
+ struct device *pdev = vibrator->input->dev.parent;
+ struct pwm_state state;
+ int err;
+
+ dev_dbg(pdev, "start vibrator with level=0x%04x", vibrator->level);
+
+ err = regulator_enable(vibrator->vcc);
+ if (err) {
+ dev_err(pdev, "failed to enable regulator: %d", err);
+ return err;
+ }
+
+ pwm_get_state(vibrator->pwm, &state);
+ state.enabled = true;
+
+ vibrator->hw->setup_pwm(vibrator->level, &state);
+ dev_dbg(pdev, "period=%u", state.period);
+
+ err = pwm_apply_state(vibrator->pwm, &state);
+ if (err) {
+ dev_err(pdev, "failed to apply pwm state: %d", err);
+ return err;
+ }
+
+ if (vibrator->pwm_dir) {
+ pwm_get_state(vibrator->pwm_dir, &state);
+ state.enabled = true;
+
+ /* always control via period */
+ vibrator->hw->setup_pwm_dir(vibrator->level, &state);
+
+ err = pwm_apply_state(vibrator->pwm_dir, &state);
+ if (err) {
+ dev_err(pdev, "failed to apply dir-pwm state: %d", err);
+ pwm_disable(vibrator->pwm);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void pwm_vibrator_stop(struct pwm_vibrator *vibrator)
+{
+ struct device *pdev = vibrator->input->dev.parent;
+
+ dev_dbg(pdev, "stop vibrator");
+
+ regulator_disable(vibrator->vcc);
+
+ if (vibrator->pwm_dir)
+ pwm_disable(vibrator->pwm_dir);
+ pwm_disable(vibrator->pwm);
+}
+
+static void vibra_play_work(struct work_struct *work)
+{
+ struct pwm_vibrator *vibrator = container_of(work,
+ struct pwm_vibrator, play_work);
+
+ if (vibrator->level)
+ pwm_vibrator_start(vibrator);
+ else
+ pwm_vibrator_stop(vibrator);
+}
+
+static int pwm_vibrator_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct pwm_vibrator *vibrator = input_get_drvdata(dev);
+
+ vibrator->level = effect->u.rumble.strong_magnitude;
+ if (!vibrator->level)
+ vibrator->level = effect->u.rumble.weak_magnitude;
+
+ schedule_work(&vibrator->play_work);
+
+ return 0;
+}
+
+static void pwm_vibrator_close(struct input_dev *input)
+{
+ struct pwm_vibrator *vibrator = input_get_drvdata(input);
+
+ cancel_work_sync(&vibrator->play_work);
+ pwm_vibrator_stop(vibrator);
+}
+
+static int pwm_vibrator_probe(struct platform_device *pdev)
+{
+ struct pwm_vibrator *vibrator;
+ struct input_dev *input;
+ struct pwm_state state;
+ int err;
+
+ vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
+ if (!vibrator)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
+ return -ENOMEM;
+
+ vibrator->input = input;
+
+ vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
+ err = PTR_ERR_OR_ZERO(vibrator->vcc);
+ if (err) {
+ if (err != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to request regulator: %d",
+ err);
+ return err;
+ }
+
+ vibrator->pwm = devm_pwm_get(&pdev->dev, "enable");
+ err = PTR_ERR_OR_ZERO(vibrator->pwm);
+ if (err) {
+ if (err != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to request main pwm: %d",
+ err);
+ return err;
+ }
+
+ INIT_WORK(&vibrator->play_work, vibra_play_work);
+
+ /* Sync up PWM state and ensure it is off. */
+ pwm_init_state(vibrator->pwm, &state);
+ state.enabled = false;
+ err = pwm_apply_state(vibrator->pwm, &state);
+ if (err) {
+ dev_err(&pdev->dev, "failed to apply initial PWM state: %d",
+ err);
+ return err;
+ }
+
+ vibrator->pwm_dir = devm_pwm_get(&pdev->dev, "direction");
+ err = PTR_ERR_OR_ZERO(vibrator->pwm_dir);
+ if (err == -ENODATA) {
+ vibrator->pwm_dir = NULL;
+ } else if (err == -EPROBE_DEFER) {
+ return err;
+ } else if (err) {
+ dev_err(&pdev->dev, "Failed to request direction pwm: %d", err);
+ return err;
+ } else {
+ /* Sync up PWM state and ensure it is off. */
+ pwm_init_state(vibrator->pwm_dir, &state);
+ state.enabled = false;
+ err = pwm_apply_state(vibrator->pwm_dir, &state);
+ if (err) {
+ dev_err(&pdev->dev, "failed to apply initial PWM state: %d",
+ err);
+ return err;
+ }
+ }
+
+ vibrator->hw = of_device_get_match_data(&pdev->dev);
+ if (!vibrator->hw)
+ vibrator->hw = &pwm_vib_hw_generic;
+
+ input->name = "pwm-vibrator";
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &pdev->dev;
+ input->close = pwm_vibrator_close;
+
+ input_set_drvdata(input, vibrator);
+ input_set_capability(input, EV_FF, FF_RUMBLE);
+
+ err = input_ff_create_memless(input, NULL, pwm_vibrator_play_effect);
+ if (err) {
+ dev_err(&pdev->dev, "Couldn't create FF dev: %d", err);
+ return err;
+ }
+
+ err = input_register_device(input);
+ if (err) {
+ dev_err(&pdev->dev, "Couldn't register input dev: %d", err);
+ return err;
+ }
+
+ platform_set_drvdata(pdev, vibrator);
+
+ return 0;
+}
+
+static int __maybe_unused pwm_vibrator_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pwm_vibrator *vibrator = platform_get_drvdata(pdev);
+ struct input_dev *input = vibrator->input;
+ unsigned long flags;
+
+ spin_lock_irqsave(&input->event_lock, flags);
+ cancel_work_sync(&vibrator->play_work);
+ if (vibrator->level)
+ pwm_vibrator_stop(vibrator);
+ spin_unlock_irqrestore(&input->event_lock, flags);
+
+ return 0;
+}
+
+static int __maybe_unused pwm_vibrator_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pwm_vibrator *vibrator = platform_get_drvdata(pdev);
+ struct input_dev *input = vibrator->input;
+ unsigned long flags;
+
+ spin_lock_irqsave(&input->event_lock, flags);
+ if (vibrator->level)
+ pwm_vibrator_start(vibrator);
+ spin_unlock_irqrestore(&input->event_lock, flags);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pwm_vibrator_pm_ops,
+ pwm_vibrator_suspend, pwm_vibrator_resume);
+
+#ifdef CONFIG_OF
+
+#define PWM_VIB_COMPAT(of_compatible, cfg) { \
+ .compatible = of_compatible, \
+ .data = &cfg, \
+}
+
+static const struct of_device_id pwm_vibra_dt_match_table[] = {
+ PWM_VIB_COMPAT("pwm-vibrator", pwm_vib_hw_generic),
+ PWM_VIB_COMPAT("motorola,mapphone-pwm-vibrator", pwm_vib_hw_mapphone),
+ {},
+};
+MODULE_DEVICE_TABLE(of, pwm_vibra_dt_match_table);
+#endif
+
+static struct platform_driver pwm_vibrator_driver = {
+ .probe = pwm_vibrator_probe,
+ .driver = {
+ .name = "pwm-vibrator",
+ .pm = &pwm_vibrator_pm_ops,
+ .of_match_table = of_match_ptr(pwm_vibra_dt_match_table),
+ },
+};
+module_platform_driver(pwm_vibrator_driver);
+
+MODULE_AUTHOR("Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>");
+MODULE_DESCRIPTION("PWM vibrator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pwm-vibrator");
--
2.11.0
--
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/2] PWM Vibrator driver
From: Sebastian Reichel @ 2017-05-03 11:11 UTC (permalink / raw)
To: Sebastian Reichel, Dmitry Torokhov, Tony Lindgren
Cc: Rob Herring, linux-input, linux-omap, devicetree, linux-kernel,
Sebastian Reichel
Hi,
The Motorola Droid 4 has a vibrator, that is connected
to two GPIOs. Motorola's stock kernel names them vib_en
and vib_dir, which probably stand for vibrator_enable
and vibrator_direction. In their stock kernel both GPIOs
are toggled using a hrtimer and a custom vibrator "misc"
device is provided to userspace.
Thankfully the hardware designers the used GPIOs can
also be used from OMAP's dmtimers, so that they can
be driven as PWM output instead saving some CPU cycles
(and code).
The driver is loosely based on an old patch from Dmitry,
that I found in the internet(tm) [0]. Note, that I did
not check the generic vibrator stuff. I just kept it in
the driver, since it's probably what other people expect
from a pwm-vibra driver :)
Also I wrote a small tool to test the vibrator running
at different strength levels, since fftest(1) used a
fixed one.
[0] https://lkml.org/lkml/2012/4/10/41
[1] https://git.collabora.com/cgit/user/sre/rumble-test.git/plain/rumble-test.c
-- Sebastian
Sebastian Reichel (2):
Input: pwm-vibra: new driver
ARM: dts: omap4-droid4: Add vibrator
.../devicetree/bindings/input/pwm-vibrator.txt | 60 ++++
arch/arm/boot/dts/omap4-droid4-xt894.dts | 38 +++
drivers/input/misc/Kconfig | 11 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/pwm-vibra.c | 343 +++++++++++++++++++++
5 files changed, 453 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/pwm-vibrator.txt
create mode 100644 drivers/input/misc/pwm-vibra.c
--
2.11.0
^ permalink raw reply
* Re: [PATCH] drivers/of_iommu: ignore SMMU DT nodes with status 'disabled'
From: Ard Biesheuvel @ 2017-05-03 10:58 UTC (permalink / raw)
To: Robin Murphy
Cc: Will Deacon,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
joro-zLv9SwRftAIdnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Mark Rutland
In-Reply-To: <1c0463e6-f82f-1179-4f54-b2d3de63dc87-5wv7dgnIgG8@public.gmane.org>
> On 3 May 2017, at 11:32, Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org> wrote:
>
>> On 28/04/17 14:22, Ard Biesheuvel wrote:
>>> On 28 April 2017 at 14:17, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>>>> On Fri, Apr 28, 2017 at 02:14:49PM +0100, Ard Biesheuvel wrote:
>>>>> On 28 April 2017 at 14:11, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>>>>> Hi Ard,
>>>>>
>>>>> [+ devicetree@]
>>>>>
>>>>>> On Fri, Apr 14, 2017 at 01:43:15PM +0100, Ard Biesheuvel wrote:
>>>>>> DT nodes may have a status property, and if they do, such nodes should
>>>>>> only be considered present if the status property is set to 'okay'.
>>>>>>
>>>>>> Currently, we call the init function of IOMMUs described by the device
>>>>>> tree without taking this into account, which may result in the output
>>>>>> below on systems where some SMMUs may be legally disabled.
>>>>>>
>>>>>> Failed to initialise IOMMU /smb/smmu@e0200000
>>>>>> Failed to initialise IOMMU /smb/smmu@e0c00000
>>>>>> arm-smmu e0a00000.smmu: probing hardware configuration...
>>>>>> arm-smmu e0a00000.smmu: SMMUv1 with:
>>>>>> arm-smmu e0a00000.smmu: stage 2 translation
>>>>>> arm-smmu e0a00000.smmu: coherent table walk
>>>>>> arm-smmu e0a00000.smmu: stream matching with 32 register groups, mask 0x7fff
>>>>>> arm-smmu e0a00000.smmu: 8 context banks (8 stage-2 only)
>>>>>> arm-smmu e0a00000.smmu: Supported page sizes: 0x60211000
>>>>>> arm-smmu e0a00000.smmu: Stage-2: 40-bit IPA -> 40-bit PA
>>>>>> Failed to initialise IOMMU /smb/smmu@e0600000
>>>>>> Failed to initialise IOMMU /smb/smmu@e0800000
>>>>>>
>>>>>> Since this is not an error condition, only call the init function if
>>>>>> the device is enabled, which also inhibits the spurious error messages.
>>>>>>
>>>>>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>>>>>> ---
>>>>>> drivers/iommu/of_iommu.c | 2 +-
>>>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
>>>>>> index 2683e9fc0dcf..2dd1206e6c0d 100644
>>>>>> --- a/drivers/iommu/of_iommu.c
>>>>>> +++ b/drivers/iommu/of_iommu.c
>>>>>> @@ -183,7 +183,7 @@ static int __init of_iommu_init(void)
>>>>>> for_each_matching_node_and_match(np, matches, &match) {
>>>>>> const of_iommu_init_fn init_fn = match->data;
>>>>>>
>>>>>> - if (init_fn(np))
>>>>>> + if (of_device_is_available(np) && init_fn(np))
>>>>>> pr_err("Failed to initialise IOMMU %s\n",
>>>>>> of_node_full_name(np));
>>>>>> }
>>>>>
>>>>> Is there a definition of what status = "disabled" is supposed to mean for an
>>>>> IOMMU? For example, that could mean that the firmware has pre-programmed the
>>>>> SMMU with particular translations or memory attributes (a bit like the
>>>>> CCA=1, CPM=1, DACS=0 case in ACPI IORT), or even disabled DMA traffic
>>>>> altogether.
>>>>>
>>>>> So I think we'd need an update to the generic IOMMU binding text to say
>>>>> exactly what the semantics are supposed to be here.
>>>>>
>>>>
>>>> I agree that it might make sense to describe the behavior of the IOMMU
>>>> when it is left in the state we found it in. But that is not the same
>>>> as status=disabled.
>>>>
>>>> The DTS subtree contains loads and loads of boilerplate
>>>> configurations, where only some pieces are enabled in the final image
>>>> by setting status=okay. So a node that has status 'disabled' should be
>>>> treated as 'not present', not as 'present but can be ignored under
>>>> assumptions such and such'
>>>>
>>>> In other words, I think we are talking about two different issues here.
>>>
>>> I'm not so sure... if we have a master device that has an iommus= property
>>> pointing to an IOMMU with status="disabled", I really don't know whether we
>>> should:
>>>
>>> 1. Assume the master can do DMA with a 1:1 mapping of memory and no
>>> changes to memory attributes
>>>
>>> 2. Assume the master can do DMA with a 1:1 mapping of memory, but
>>> potentially with changes to the attributes
>>>
>>> 3. Assume the master can do DMA, but with some pre-existing translation
>>> (what?)
>>>
>>> 4. Assume the master can't do DMA
>>>
>>> and I also don't know whether the "dma-coherent" property remains valid.
>>>
>>
>> Ah yes. Good point.
>>
>> So indeed, there should be some IOMMU specific status property that
>> can convey all of the above, or 1. and 4. at the minimum
>
> FWIW, the underlying issue being addressed here should be going away now
> anyway, since the now-queued probe deferral series obviates the init_fn
> early-device-creation bodge. I've been deliberately ignoring it for some
> time for precisely that reason ;)
>
Ok. I have also updated the Seattle firmware to remove the smmu nodes and the associated iommus/iommu-map properties entirely when disabling SMMU support in the firmware, which should address Will's concern regarding unspecified behavior of a disabled SMMU.
IOW, this patch can be disregarded. Thanks.
--
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 1/2] extcon: usb-gpio: Add level trigger support
From: Baolin Wang @ 2017-05-03 10:56 UTC (permalink / raw)
To: Rob Herring
Cc: Chanwoo Choi, MyungJoo Ham, Mark Rutland, Mark Brown,
Linaro Kernel Mailman List, LKML,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAMz4kuLi0rP7UP93hGZoO2WKTDTA08LQ3jYgL14qoaudV6rW+g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Rob,
On 29 March 2017 at 14:08, Baolin Wang <baolin.wang-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> Hi,
>
> On 24 March 2017 at 20:41, Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>> On Fri, Mar 24, 2017 at 6:56 AM, Baolin Wang <baolin.wang-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>>> Hi,
>>>
>>> On 24 March 2017 at 19:08, Chanwoo Choi <cw00.choi-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> wrote:
>>>> Hi,
>>>>
>>>> On 2017년 03월 20일 16:59, Baolin Wang wrote:
>>>>> Now extcon-usb-gpio only supports for GPIO egdge trigger, but VBUS/ID
>>>>> gpios' detection can be triggered by the level trigger on some platforms.
>>>>> Thus intoduce one property 'extcon-gpio,level-trigger' to identify this
>>>>> situation.
>>>>>
>>>>> Signed-off-by: Baolin Wang <baolin.wang-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>>>>> ---
>>>>> .../devicetree/bindings/extcon/extcon-usb-gpio.txt | 3 +++
>>>>> 1 file changed, 3 insertions(+)
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
>>>>> index dfc14f7..191504b 100644
>>>>> --- a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
>>>>> +++ b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
>>>>> @@ -9,6 +9,9 @@ Required properties:
>>>>> Either one of id-gpio or vbus-gpio must be present. Both can be present as well.
>>>>> - id-gpio: gpio for USB ID pin. See gpio binding.
>>>>> - vbus-gpio: gpio for USB VBUS pin.
>>>>> +- extcon-gpio,level-trigger: Boolean, set this gpio's interrupt flag to
>>>>
>>>> It is 'extcon-usb-gpio' instead of 'extcon-gpio'.
>>>
>>> Sure.
>>
>> "extcon-gpio" is not a vendor, so don't make it a vendor prefix.
>
> OK.
>
>>
>>>>> +level trigger. If not specified defaults to false, gpio's interrupt flag
>>>>> +defaults to edge trigger.
>>>>
>>>> I understand why you need the new flag.
>>>> But, I'm not sure it is right way to add the new flag for interrupt flag.
>>>>
>>>> IMHO, I think that we need to find the more proper way to get the interrupt flag
>>>> or maybe, we may need to implement the new helper api to get the interrupt
>>>> flag for gpio pin when we use the gpio as the interrupt source,
>>>
>>> Yes, I agree with you. We already have helper functions to get
>>> interrupt flag from device tree, but we can not get the irq number of
>>> GPIO to configure in device tree, then I introduce one new flag to
>>> indicate the trigger type. But I like to change the patch if there are
>>> any good suggestion. Thanks.
>>
>> The extcon binding needs an overhaul in general. I'm not going to take
>> extensions to a broken binding. What's needed is a USB connector
>> binding like we have for HDMI and other video connectors. And for
>> USB-C, those need to be combined.
Could you elaborate on how to make an overhaul for the extcon binding?
I can help to do that and then we can consider to modify my patch.
Thanks.
>
> I am not sure I understand your points correctly, could you elaborate
> on how to modify for extcon? Thanks.
>
> --
> Baolin.wang
> Best Regards
--
Baolin.wang
Best Regards
--
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] drivers/of_iommu: ignore SMMU DT nodes with status 'disabled'
From: Robin Murphy @ 2017-05-03 10:32 UTC (permalink / raw)
To: Ard Biesheuvel, Will Deacon
Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
In-Reply-To: <CAKv+Gu8L1iyzBitt8zzvWDDEXk_ysGfPRxHJbQQK5o8NMWF5MA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 28/04/17 14:22, Ard Biesheuvel wrote:
> On 28 April 2017 at 14:17, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>> On Fri, Apr 28, 2017 at 02:14:49PM +0100, Ard Biesheuvel wrote:
>>> On 28 April 2017 at 14:11, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>>>> Hi Ard,
>>>>
>>>> [+ devicetree@]
>>>>
>>>> On Fri, Apr 14, 2017 at 01:43:15PM +0100, Ard Biesheuvel wrote:
>>>>> DT nodes may have a status property, and if they do, such nodes should
>>>>> only be considered present if the status property is set to 'okay'.
>>>>>
>>>>> Currently, we call the init function of IOMMUs described by the device
>>>>> tree without taking this into account, which may result in the output
>>>>> below on systems where some SMMUs may be legally disabled.
>>>>>
>>>>> Failed to initialise IOMMU /smb/smmu@e0200000
>>>>> Failed to initialise IOMMU /smb/smmu@e0c00000
>>>>> arm-smmu e0a00000.smmu: probing hardware configuration...
>>>>> arm-smmu e0a00000.smmu: SMMUv1 with:
>>>>> arm-smmu e0a00000.smmu: stage 2 translation
>>>>> arm-smmu e0a00000.smmu: coherent table walk
>>>>> arm-smmu e0a00000.smmu: stream matching with 32 register groups, mask 0x7fff
>>>>> arm-smmu e0a00000.smmu: 8 context banks (8 stage-2 only)
>>>>> arm-smmu e0a00000.smmu: Supported page sizes: 0x60211000
>>>>> arm-smmu e0a00000.smmu: Stage-2: 40-bit IPA -> 40-bit PA
>>>>> Failed to initialise IOMMU /smb/smmu@e0600000
>>>>> Failed to initialise IOMMU /smb/smmu@e0800000
>>>>>
>>>>> Since this is not an error condition, only call the init function if
>>>>> the device is enabled, which also inhibits the spurious error messages.
>>>>>
>>>>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>>>>> ---
>>>>> drivers/iommu/of_iommu.c | 2 +-
>>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
>>>>> index 2683e9fc0dcf..2dd1206e6c0d 100644
>>>>> --- a/drivers/iommu/of_iommu.c
>>>>> +++ b/drivers/iommu/of_iommu.c
>>>>> @@ -183,7 +183,7 @@ static int __init of_iommu_init(void)
>>>>> for_each_matching_node_and_match(np, matches, &match) {
>>>>> const of_iommu_init_fn init_fn = match->data;
>>>>>
>>>>> - if (init_fn(np))
>>>>> + if (of_device_is_available(np) && init_fn(np))
>>>>> pr_err("Failed to initialise IOMMU %s\n",
>>>>> of_node_full_name(np));
>>>>> }
>>>>
>>>> Is there a definition of what status = "disabled" is supposed to mean for an
>>>> IOMMU? For example, that could mean that the firmware has pre-programmed the
>>>> SMMU with particular translations or memory attributes (a bit like the
>>>> CCA=1, CPM=1, DACS=0 case in ACPI IORT), or even disabled DMA traffic
>>>> altogether.
>>>>
>>>> So I think we'd need an update to the generic IOMMU binding text to say
>>>> exactly what the semantics are supposed to be here.
>>>>
>>>
>>> I agree that it might make sense to describe the behavior of the IOMMU
>>> when it is left in the state we found it in. But that is not the same
>>> as status=disabled.
>>>
>>> The DTS subtree contains loads and loads of boilerplate
>>> configurations, where only some pieces are enabled in the final image
>>> by setting status=okay. So a node that has status 'disabled' should be
>>> treated as 'not present', not as 'present but can be ignored under
>>> assumptions such and such'
>>>
>>> In other words, I think we are talking about two different issues here.
>>
>> I'm not so sure... if we have a master device that has an iommus= property
>> pointing to an IOMMU with status="disabled", I really don't know whether we
>> should:
>>
>> 1. Assume the master can do DMA with a 1:1 mapping of memory and no
>> changes to memory attributes
>>
>> 2. Assume the master can do DMA with a 1:1 mapping of memory, but
>> potentially with changes to the attributes
>>
>> 3. Assume the master can do DMA, but with some pre-existing translation
>> (what?)
>>
>> 4. Assume the master can't do DMA
>>
>> and I also don't know whether the "dma-coherent" property remains valid.
>>
>
> Ah yes. Good point.
>
> So indeed, there should be some IOMMU specific status property that
> can convey all of the above, or 1. and 4. at the minimum
FWIW, the underlying issue being addressed here should be going away now
anyway, since the now-queued probe deferral series obviates the init_fn
early-device-creation bodge. I've been deliberately ignoring it for some
time for precisely that reason ;)
Robin.
^ permalink raw reply
* [PATCH v3 1/1] of: Move OF property and graph API from base.c to property.c
From: Sakari Ailus @ 2017-05-03 10:21 UTC (permalink / raw)
To: devicetree, robh, frowand.list
Cc: linux-acpi, sudeep.holla, lorenzo.pieralisi, mika.westerberg,
rafael, mark.rutland, broonie, ahs3
base.c contains both core OF functions and increasingly other
functionality such as accessing properties and graphs, including
convenience functions. In the near future this would also include OF
specific implementation of the fwnode property and graph APIs.
Create driver/of/property.c to contain procedures for accessing and
interpreting device tree properties. The procedures are moved from
drivers/of/base.c, with no changes other than copying only the includes
required by the moved procedures.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
Hi Rob and Frank,
This is a pre-dependency for further fwnode property cleanup. The patch
appears to be slightly more conflict-prone than the rest of the patchset:
<URL:http://www.spinics.net/lists/linux-acpi/msg72647.html>
Would it be possible to merge it separately from the rest?
since v2:
- Also move of_property_read_u64_index() added by patch
"drivers/of/base.c: Add of_property_read_u64_index" found in linux-next
drivers/of/Makefile | 2 +-
drivers/of/base.c | 733 ------------------------------------------------
drivers/of/property.c | 763 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 764 insertions(+), 734 deletions(-)
create mode 100644 drivers/of/property.c
Regards,
Sakari
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index d7efd9d..97dc01c 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,4 +1,4 @@
-obj-y = base.o device.o platform.o
+obj-y = base.o device.o platform.o property.o
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
obj-$(CONFIG_OF_FLATTREE) += fdt.o
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 0ea16bd..d57188d 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1113,458 +1113,6 @@ struct device_node *of_find_node_by_phandle(phandle handle)
}
EXPORT_SYMBOL(of_find_node_by_phandle);
-/**
- * of_property_count_elems_of_size - Count the number of elements in a property
- *
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @elem_size: size of the individual element
- *
- * Search for a property in a device node and count the number of elements of
- * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
- * property does not exist or its length does not match a multiple of elem_size
- * and -ENODATA if the property does not have a value.
- */
-int of_property_count_elems_of_size(const struct device_node *np,
- const char *propname, int elem_size)
-{
- struct property *prop = of_find_property(np, propname, NULL);
-
- if (!prop)
- return -EINVAL;
- if (!prop->value)
- return -ENODATA;
-
- if (prop->length % elem_size != 0) {
- pr_err("size of %s in node %s is not a multiple of %d\n",
- propname, np->full_name, elem_size);
- return -EINVAL;
- }
-
- return prop->length / elem_size;
-}
-EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
-
-/**
- * of_find_property_value_of_size
- *
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @min: minimum allowed length of property value
- * @max: maximum allowed length of property value (0 means unlimited)
- * @len: if !=NULL, actual length is written to here
- *
- * Search for a property in a device node and valid the requested size.
- * Returns the property value on success, -EINVAL if the property does not
- * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data is too small or too large.
- *
- */
-static void *of_find_property_value_of_size(const struct device_node *np,
- const char *propname, u32 min, u32 max, size_t *len)
-{
- struct property *prop = of_find_property(np, propname, NULL);
-
- if (!prop)
- return ERR_PTR(-EINVAL);
- if (!prop->value)
- return ERR_PTR(-ENODATA);
- if (prop->length < min)
- return ERR_PTR(-EOVERFLOW);
- if (max && prop->length > max)
- return ERR_PTR(-EOVERFLOW);
-
- if (len)
- *len = prop->length;
-
- return prop->value;
-}
-
-/**
- * of_property_read_u32_index - Find and read a u32 from a multi-value property.
- *
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @index: index of the u32 in the list of values
- * @out_value: pointer to return value, modified only if no error.
- *
- * Search for a property in a device node and read nth 32-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u32 value can be decoded.
- */
-int of_property_read_u32_index(const struct device_node *np,
- const char *propname,
- u32 index, u32 *out_value)
-{
- const u32 *val = of_find_property_value_of_size(np, propname,
- ((index + 1) * sizeof(*out_value)),
- 0,
- NULL);
-
- if (IS_ERR(val))
- return PTR_ERR(val);
-
- *out_value = be32_to_cpup(((__be32 *)val) + index);
- return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u32_index);
-
-/**
- * of_property_read_u64_index - Find and read a u64 from a multi-value property.
- *
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @index: index of the u64 in the list of values
- * @out_value: pointer to return value, modified only if no error.
- *
- * Search for a property in a device node and read nth 64-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_u64_index(const struct device_node *np,
- const char *propname,
- u32 index, u64 *out_value)
-{
- const u64 *val = of_find_property_value_of_size(np, propname,
- ((index + 1) * sizeof(*out_value)),
- 0, NULL);
-
- if (IS_ERR(val))
- return PTR_ERR(val);
-
- *out_value = be64_to_cpup(((__be64 *)val) + index);
- return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u64_index);
-
-/**
- * of_property_read_variable_u8_array - Find and read an array of u8 from a
- * property, with bounds on the minimum and maximum array size.
- *
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @out_values: pointer to return value, modified only if return value is 0.
- * @sz_min: minimum number of array elements to read
- * @sz_max: maximum number of array elements to read, if zero there is no
- * upper limit on the number of elements in the dts entry but only
- * sz_min will be read.
- *
- * Search for a property in a device node and read 8-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * dts entry of array should be like:
- * property = /bits/ 8 <0x50 0x60 0x70>;
- *
- * The out_values is modified only if a valid u8 value can be decoded.
- */
-int of_property_read_variable_u8_array(const struct device_node *np,
- const char *propname, u8 *out_values,
- size_t sz_min, size_t sz_max)
-{
- size_t sz, count;
- const u8 *val = of_find_property_value_of_size(np, propname,
- (sz_min * sizeof(*out_values)),
- (sz_max * sizeof(*out_values)),
- &sz);
-
- if (IS_ERR(val))
- return PTR_ERR(val);
-
- if (!sz_max)
- sz = sz_min;
- else
- sz /= sizeof(*out_values);
-
- count = sz;
- while (count--)
- *out_values++ = *val++;
-
- return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array);
-
-/**
- * of_property_read_variable_u16_array - Find and read an array of u16 from a
- * property, with bounds on the minimum and maximum array size.
- *
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @out_values: pointer to return value, modified only if return value is 0.
- * @sz_min: minimum number of array elements to read
- * @sz_max: maximum number of array elements to read, if zero there is no
- * upper limit on the number of elements in the dts entry but only
- * sz_min will be read.
- *
- * Search for a property in a device node and read 16-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * dts entry of array should be like:
- * property = /bits/ 16 <0x5000 0x6000 0x7000>;
- *
- * The out_values is modified only if a valid u16 value can be decoded.
- */
-int of_property_read_variable_u16_array(const struct device_node *np,
- const char *propname, u16 *out_values,
- size_t sz_min, size_t sz_max)
-{
- size_t sz, count;
- const __be16 *val = of_find_property_value_of_size(np, propname,
- (sz_min * sizeof(*out_values)),
- (sz_max * sizeof(*out_values)),
- &sz);
-
- if (IS_ERR(val))
- return PTR_ERR(val);
-
- if (!sz_max)
- sz = sz_min;
- else
- sz /= sizeof(*out_values);
-
- count = sz;
- while (count--)
- *out_values++ = be16_to_cpup(val++);
-
- return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array);
-
-/**
- * of_property_read_variable_u32_array - Find and read an array of 32 bit
- * integers from a property, with bounds on the minimum and maximum array size.
- *
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @out_values: pointer to return value, modified only if return value is 0.
- * @sz_min: minimum number of array elements to read
- * @sz_max: maximum number of array elements to read, if zero there is no
- * upper limit on the number of elements in the dts entry but only
- * sz_min will be read.
- *
- * Search for a property in a device node and read 32-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * The out_values is modified only if a valid u32 value can be decoded.
- */
-int of_property_read_variable_u32_array(const struct device_node *np,
- const char *propname, u32 *out_values,
- size_t sz_min, size_t sz_max)
-{
- size_t sz, count;
- const __be32 *val = of_find_property_value_of_size(np, propname,
- (sz_min * sizeof(*out_values)),
- (sz_max * sizeof(*out_values)),
- &sz);
-
- if (IS_ERR(val))
- return PTR_ERR(val);
-
- if (!sz_max)
- sz = sz_min;
- else
- sz /= sizeof(*out_values);
-
- count = sz;
- while (count--)
- *out_values++ = be32_to_cpup(val++);
-
- return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u32_array);
-
-/**
- * of_property_read_u64 - Find and read a 64 bit integer from a property
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @out_value: pointer to return value, modified only if return value is 0.
- *
- * Search for a property in a device node and read a 64-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_u64(const struct device_node *np, const char *propname,
- u64 *out_value)
-{
- const __be32 *val = of_find_property_value_of_size(np, propname,
- sizeof(*out_value),
- 0,
- NULL);
-
- if (IS_ERR(val))
- return PTR_ERR(val);
-
- *out_value = of_read_number(val, 2);
- return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u64);
-
-/**
- * of_property_read_variable_u64_array - Find and read an array of 64 bit
- * integers from a property, with bounds on the minimum and maximum array size.
- *
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @out_values: pointer to return value, modified only if return value is 0.
- * @sz_min: minimum number of array elements to read
- * @sz_max: maximum number of array elements to read, if zero there is no
- * upper limit on the number of elements in the dts entry but only
- * sz_min will be read.
- *
- * Search for a property in a device node and read 64-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * The out_values is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_variable_u64_array(const struct device_node *np,
- const char *propname, u64 *out_values,
- size_t sz_min, size_t sz_max)
-{
- size_t sz, count;
- const __be32 *val = of_find_property_value_of_size(np, propname,
- (sz_min * sizeof(*out_values)),
- (sz_max * sizeof(*out_values)),
- &sz);
-
- if (IS_ERR(val))
- return PTR_ERR(val);
-
- if (!sz_max)
- sz = sz_min;
- else
- sz /= sizeof(*out_values);
-
- count = sz;
- while (count--) {
- *out_values++ = of_read_number(val, 2);
- val += 2;
- }
-
- return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u64_array);
-
-/**
- * of_property_read_string - Find and read a string from a property
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @out_string: pointer to null terminated return string, modified only if
- * return value is 0.
- *
- * Search for a property in a device tree node and retrieve a null
- * terminated string value (pointer to data, not a copy). Returns 0 on
- * success, -EINVAL if the property does not exist, -ENODATA if property
- * does not have a value, and -EILSEQ if the string is not null-terminated
- * within the length of the property data.
- *
- * The out_string pointer is modified only if a valid string can be decoded.
- */
-int of_property_read_string(const struct device_node *np, const char *propname,
- const char **out_string)
-{
- const struct property *prop = of_find_property(np, propname, NULL);
- if (!prop)
- return -EINVAL;
- if (!prop->value)
- return -ENODATA;
- if (strnlen(prop->value, prop->length) >= prop->length)
- return -EILSEQ;
- *out_string = prop->value;
- return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_string);
-
-/**
- * of_property_match_string() - Find string in a list and return index
- * @np: pointer to node containing string list property
- * @propname: string list property name
- * @string: pointer to string to search for in string list
- *
- * This function searches a string list property and returns the index
- * of a specific string value.
- */
-int of_property_match_string(const struct device_node *np, const char *propname,
- const char *string)
-{
- const struct property *prop = of_find_property(np, propname, NULL);
- size_t l;
- int i;
- const char *p, *end;
-
- if (!prop)
- return -EINVAL;
- if (!prop->value)
- return -ENODATA;
-
- p = prop->value;
- end = p + prop->length;
-
- for (i = 0; p < end; i++, p += l) {
- l = strnlen(p, end - p) + 1;
- if (p + l > end)
- return -EILSEQ;
- pr_debug("comparing %s with %s\n", string, p);
- if (strcmp(string, p) == 0)
- return i; /* Found it; return index */
- }
- return -ENODATA;
-}
-EXPORT_SYMBOL_GPL(of_property_match_string);
-
-/**
- * of_property_read_string_helper() - Utility helper for parsing string properties
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @out_strs: output array of string pointers.
- * @sz: number of array elements to read.
- * @skip: Number of strings to skip over at beginning of list.
- *
- * Don't call this function directly. It is a utility helper for the
- * of_property_read_string*() family of functions.
- */
-int of_property_read_string_helper(const struct device_node *np,
- const char *propname, const char **out_strs,
- size_t sz, int skip)
-{
- const struct property *prop = of_find_property(np, propname, NULL);
- int l = 0, i = 0;
- const char *p, *end;
-
- if (!prop)
- return -EINVAL;
- if (!prop->value)
- return -ENODATA;
- p = prop->value;
- end = p + prop->length;
-
- for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
- l = strnlen(p, end - p) + 1;
- if (p + l > end)
- return -EILSEQ;
- if (out_strs && i >= skip)
- *out_strs++ = p;
- }
- i -= skip;
- return i <= 0 ? -ENODATA : i;
-}
-EXPORT_SYMBOL_GPL(of_property_read_string_helper);
-
void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
{
int i;
@@ -2211,47 +1759,6 @@ int of_alias_get_highest_id(const char *stem)
}
EXPORT_SYMBOL_GPL(of_alias_get_highest_id);
-const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
- u32 *pu)
-{
- const void *curv = cur;
-
- if (!prop)
- return NULL;
-
- if (!cur) {
- curv = prop->value;
- goto out_val;
- }
-
- curv += sizeof(*cur);
- if (curv >= prop->value + prop->length)
- return NULL;
-
-out_val:
- *pu = be32_to_cpup(curv);
- return curv;
-}
-EXPORT_SYMBOL_GPL(of_prop_next_u32);
-
-const char *of_prop_next_string(struct property *prop, const char *cur)
-{
- const void *curv = cur;
-
- if (!prop)
- return NULL;
-
- if (!cur)
- return prop->value;
-
- curv += strlen(cur) + 1;
- if (curv >= prop->value + prop->length)
- return NULL;
-
- return curv;
-}
-EXPORT_SYMBOL_GPL(of_prop_next_string);
-
/**
* of_console_check() - Test and setup console for DT setup
* @dn - Pointer to device node
@@ -2326,243 +1833,3 @@ int of_find_last_cache_level(unsigned int cpu)
return cache_level;
}
-
-/**
- * of_graph_parse_endpoint() - parse common endpoint node properties
- * @node: pointer to endpoint device_node
- * @endpoint: pointer to the OF endpoint data structure
- *
- * The caller should hold a reference to @node.
- */
-int of_graph_parse_endpoint(const struct device_node *node,
- struct of_endpoint *endpoint)
-{
- struct device_node *port_node = of_get_parent(node);
-
- WARN_ONCE(!port_node, "%s(): endpoint %s has no parent node\n",
- __func__, node->full_name);
-
- memset(endpoint, 0, sizeof(*endpoint));
-
- endpoint->local_node = node;
- /*
- * It doesn't matter whether the two calls below succeed.
- * If they don't then the default value 0 is used.
- */
- of_property_read_u32(port_node, "reg", &endpoint->port);
- of_property_read_u32(node, "reg", &endpoint->id);
-
- of_node_put(port_node);
-
- return 0;
-}
-EXPORT_SYMBOL(of_graph_parse_endpoint);
-
-/**
- * of_graph_get_port_by_id() - get the port matching a given id
- * @parent: pointer to the parent device node
- * @id: id of the port
- *
- * Return: A 'port' node pointer with refcount incremented. The caller
- * has to use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
-{
- struct device_node *node, *port;
-
- node = of_get_child_by_name(parent, "ports");
- if (node)
- parent = node;
-
- for_each_child_of_node(parent, port) {
- u32 port_id = 0;
-
- if (of_node_cmp(port->name, "port") != 0)
- continue;
- of_property_read_u32(port, "reg", &port_id);
- if (id == port_id)
- break;
- }
-
- of_node_put(node);
-
- return port;
-}
-EXPORT_SYMBOL(of_graph_get_port_by_id);
-
-/**
- * of_graph_get_next_endpoint() - get next endpoint node
- * @parent: pointer to the parent device node
- * @prev: previous endpoint node, or NULL to get first
- *
- * Return: An 'endpoint' node pointer with refcount incremented. Refcount
- * of the passed @prev node is decremented.
- */
-struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
- struct device_node *prev)
-{
- struct device_node *endpoint;
- struct device_node *port;
-
- if (!parent)
- return NULL;
-
- /*
- * Start by locating the port node. If no previous endpoint is specified
- * search for the first port node, otherwise get the previous endpoint
- * parent port node.
- */
- if (!prev) {
- struct device_node *node;
-
- node = of_get_child_by_name(parent, "ports");
- if (node)
- parent = node;
-
- port = of_get_child_by_name(parent, "port");
- of_node_put(node);
-
- if (!port) {
- pr_err("graph: no port node found in %s\n",
- parent->full_name);
- return NULL;
- }
- } else {
- port = of_get_parent(prev);
- if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
- __func__, prev->full_name))
- return NULL;
- }
-
- while (1) {
- /*
- * Now that we have a port node, get the next endpoint by
- * getting the next child. If the previous endpoint is NULL this
- * will return the first child.
- */
- endpoint = of_get_next_child(port, prev);
- if (endpoint) {
- of_node_put(port);
- return endpoint;
- }
-
- /* No more endpoints under this port, try the next one. */
- prev = NULL;
-
- do {
- port = of_get_next_child(parent, port);
- if (!port)
- return NULL;
- } while (of_node_cmp(port->name, "port"));
- }
-}
-EXPORT_SYMBOL(of_graph_get_next_endpoint);
-
-/**
- * of_graph_get_endpoint_by_regs() - get endpoint node of specific identifiers
- * @parent: pointer to the parent device node
- * @port_reg: identifier (value of reg property) of the parent port node
- * @reg: identifier (value of reg property) of the endpoint node
- *
- * Return: An 'endpoint' node pointer which is identified by reg and at the same
- * is the child of a port node identified by port_reg. reg and port_reg are
- * ignored when they are -1.
- */
-struct device_node *of_graph_get_endpoint_by_regs(
- const struct device_node *parent, int port_reg, int reg)
-{
- struct of_endpoint endpoint;
- struct device_node *node = NULL;
-
- for_each_endpoint_of_node(parent, node) {
- of_graph_parse_endpoint(node, &endpoint);
- if (((port_reg == -1) || (endpoint.port == port_reg)) &&
- ((reg == -1) || (endpoint.id == reg)))
- return node;
- }
-
- return NULL;
-}
-EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
-
-/**
- * of_graph_get_remote_port_parent() - get remote port's parent node
- * @node: pointer to a local endpoint device_node
- *
- * Return: Remote device node associated with remote endpoint node linked
- * to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_port_parent(
- const struct device_node *node)
-{
- struct device_node *np;
- unsigned int depth;
-
- /* Get remote endpoint node. */
- np = of_parse_phandle(node, "remote-endpoint", 0);
-
- /* Walk 3 levels up only if there is 'ports' node. */
- for (depth = 3; depth && np; depth--) {
- np = of_get_next_parent(np);
- if (depth == 2 && of_node_cmp(np->name, "ports"))
- break;
- }
- return np;
-}
-EXPORT_SYMBOL(of_graph_get_remote_port_parent);
-
-/**
- * of_graph_get_remote_port() - get remote port node
- * @node: pointer to a local endpoint device_node
- *
- * Return: Remote port node associated with remote endpoint node linked
- * to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_port(const struct device_node *node)
-{
- struct device_node *np;
-
- /* Get remote endpoint node. */
- np = of_parse_phandle(node, "remote-endpoint", 0);
- if (!np)
- return NULL;
- return of_get_next_parent(np);
-}
-EXPORT_SYMBOL(of_graph_get_remote_port);
-
-/**
- * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
- * @node: pointer to parent device_node containing graph port/endpoint
- * @port: identifier (value of reg property) of the parent port node
- * @endpoint: identifier (value of reg property) of the endpoint node
- *
- * Return: Remote device node associated with remote endpoint node linked
- * to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_node(const struct device_node *node,
- u32 port, u32 endpoint)
-{
- struct device_node *endpoint_node, *remote;
-
- endpoint_node = of_graph_get_endpoint_by_regs(node, port, endpoint);
- if (!endpoint_node) {
- pr_debug("no valid endpoint (%d, %d) for node %s\n",
- port, endpoint, node->full_name);
- return NULL;
- }
-
- remote = of_graph_get_remote_port_parent(endpoint_node);
- of_node_put(endpoint_node);
- if (!remote) {
- pr_debug("no valid remote node\n");
- return NULL;
- }
-
- if (!of_device_is_available(remote)) {
- pr_debug("not available for remote node\n");
- return NULL;
- }
-
- return remote;
-}
-EXPORT_SYMBOL(of_graph_get_remote_node);
diff --git a/drivers/of/property.c b/drivers/of/property.c
new file mode 100644
index 0000000..e5d3e1f
--- /dev/null
+++ b/drivers/of/property.c
@@ -0,0 +1,763 @@
+/*
+ * drivers/of/property.c - Procedures for accessing and interpreting
+ * Devicetree properties and graphs.
+ *
+ * Initially created by copying procedures from drivers/of/base.c. This
+ * file contains the OF property as well as the OF graph interface
+ * functions.
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ * {engebret|bergner}@us.ibm.com
+ *
+ * Adapted for sparc and sparc64 by David S. Miller davem@davemloft.net
+ *
+ * Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell and
+ * Grant Likely.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/string.h>
+
+#include "of_private.h"
+/**
+ * of_property_count_elems_of_size - Count the number of elements in a property
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @elem_size: size of the individual element
+ *
+ * Search for a property in a device node and count the number of elements of
+ * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
+ * property does not exist or its length does not match a multiple of elem_size
+ * and -ENODATA if the property does not have a value.
+ */
+int of_property_count_elems_of_size(const struct device_node *np,
+ const char *propname, int elem_size)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ if (prop->length % elem_size != 0) {
+ pr_err("size of %s in node %s is not a multiple of %d\n",
+ propname, np->full_name, elem_size);
+ return -EINVAL;
+ }
+
+ return prop->length / elem_size;
+}
+EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
+
+/**
+ * of_find_property_value_of_size
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @min: minimum allowed length of property value
+ * @max: maximum allowed length of property value (0 means unlimited)
+ * @len: if !=NULL, actual length is written to here
+ *
+ * Search for a property in a device node and valid the requested size.
+ * Returns the property value on success, -EINVAL if the property does not
+ * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data is too small or too large.
+ *
+ */
+static void *of_find_property_value_of_size(const struct device_node *np,
+ const char *propname, u32 min, u32 max, size_t *len)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+
+ if (!prop)
+ return ERR_PTR(-EINVAL);
+ if (!prop->value)
+ return ERR_PTR(-ENODATA);
+ if (prop->length < min)
+ return ERR_PTR(-EOVERFLOW);
+ if (max && prop->length > max)
+ return ERR_PTR(-EOVERFLOW);
+
+ if (len)
+ *len = prop->length;
+
+ return prop->value;
+}
+
+/**
+ * of_property_read_u32_index - Find and read a u32 from a multi-value property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @index: index of the u32 in the list of values
+ * @out_value: pointer to return value, modified only if no error.
+ *
+ * Search for a property in a device node and read nth 32-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_u32_index(const struct device_node *np,
+ const char *propname,
+ u32 index, u32 *out_value)
+{
+ const u32 *val = of_find_property_value_of_size(np, propname,
+ ((index + 1) * sizeof(*out_value)),
+ 0,
+ NULL);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ *out_value = be32_to_cpup(((__be32 *)val) + index);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u32_index);
+
+/**
+ * of_property_read_u64_index - Find and read a u64 from a multi-value property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @index: index of the u64 in the list of values
+ * @out_value: pointer to return value, modified only if no error.
+ *
+ * Search for a property in a device node and read nth 64-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_u64_index(const struct device_node *np,
+ const char *propname,
+ u32 index, u64 *out_value)
+{
+ const u64 *val = of_find_property_value_of_size(np, propname,
+ ((index + 1) * sizeof(*out_value)),
+ 0, NULL);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ *out_value = be64_to_cpup(((__be64 *)val) + index);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u64_index);
+
+/**
+ * of_property_read_variable_u8_array - Find and read an array of u8 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 8-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * dts entry of array should be like:
+ * property = /bits/ 8 <0x50 0x60 0x70>;
+ *
+ * The out_values is modified only if a valid u8 value can be decoded.
+ */
+int of_property_read_variable_u8_array(const struct device_node *np,
+ const char *propname, u8 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const u8 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = *val++;
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array);
+
+/**
+ * of_property_read_variable_u16_array - Find and read an array of u16 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 16-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * dts entry of array should be like:
+ * property = /bits/ 16 <0x5000 0x6000 0x7000>;
+ *
+ * The out_values is modified only if a valid u16 value can be decoded.
+ */
+int of_property_read_variable_u16_array(const struct device_node *np,
+ const char *propname, u16 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const __be16 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = be16_to_cpup(val++);
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array);
+
+/**
+ * of_property_read_variable_u32_array - Find and read an array of 32 bit
+ * integers from a property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_variable_u32_array(const struct device_node *np,
+ const char *propname, u32 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--)
+ *out_values++ = be32_to_cpup(val++);
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u32_array);
+
+/**
+ * of_property_read_u64 - Find and read a 64 bit integer from a property
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_value: pointer to return value, modified only if return value is 0.
+ *
+ * Search for a property in a device node and read a 64-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_u64(const struct device_node *np, const char *propname,
+ u64 *out_value)
+{
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ sizeof(*out_value),
+ 0,
+ NULL);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ *out_value = of_read_number(val, 2);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u64);
+
+/**
+ * of_property_read_variable_u64_array - Find and read an array of 64 bit
+ * integers from a property, with bounds on the minimum and maximum array size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz_min: minimum number of array elements to read
+ * @sz_max: maximum number of array elements to read, if zero there is no
+ * upper limit on the number of elements in the dts entry but only
+ * sz_min will be read.
+ *
+ * Search for a property in a device node and read 64-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_variable_u64_array(const struct device_node *np,
+ const char *propname, u64 *out_values,
+ size_t sz_min, size_t sz_max)
+{
+ size_t sz, count;
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ (sz_min * sizeof(*out_values)),
+ (sz_max * sizeof(*out_values)),
+ &sz);
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ if (!sz_max)
+ sz = sz_min;
+ else
+ sz /= sizeof(*out_values);
+
+ count = sz;
+ while (count--) {
+ *out_values++ = of_read_number(val, 2);
+ val += 2;
+ }
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u64_array);
+
+/**
+ * of_property_read_string - Find and read a string from a property
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_string: pointer to null terminated return string, modified only if
+ * return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy). Returns 0 on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+int of_property_read_string(const struct device_node *np, const char *propname,
+ const char **out_string)
+{
+ const struct property *prop = of_find_property(np, propname, NULL);
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ if (strnlen(prop->value, prop->length) >= prop->length)
+ return -EILSEQ;
+ *out_string = prop->value;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string);
+
+/**
+ * of_property_match_string() - Find string in a list and return index
+ * @np: pointer to node containing string list property
+ * @propname: string list property name
+ * @string: pointer to string to search for in string list
+ *
+ * This function searches a string list property and returns the index
+ * of a specific string value.
+ */
+int of_property_match_string(const struct device_node *np, const char *propname,
+ const char *string)
+{
+ const struct property *prop = of_find_property(np, propname, NULL);
+ size_t l;
+ int i;
+ const char *p, *end;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ p = prop->value;
+ end = p + prop->length;
+
+ for (i = 0; p < end; i++, p += l) {
+ l = strnlen(p, end - p) + 1;
+ if (p + l > end)
+ return -EILSEQ;
+ pr_debug("comparing %s with %s\n", string, p);
+ if (strcmp(string, p) == 0)
+ return i; /* Found it; return index */
+ }
+ return -ENODATA;
+}
+EXPORT_SYMBOL_GPL(of_property_match_string);
+
+/**
+ * of_property_read_string_helper() - Utility helper for parsing string properties
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_strs: output array of string pointers.
+ * @sz: number of array elements to read.
+ * @skip: Number of strings to skip over at beginning of list.
+ *
+ * Don't call this function directly. It is a utility helper for the
+ * of_property_read_string*() family of functions.
+ */
+int of_property_read_string_helper(const struct device_node *np,
+ const char *propname, const char **out_strs,
+ size_t sz, int skip)
+{
+ const struct property *prop = of_find_property(np, propname, NULL);
+ int l = 0, i = 0;
+ const char *p, *end;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ p = prop->value;
+ end = p + prop->length;
+
+ for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
+ l = strnlen(p, end - p) + 1;
+ if (p + l > end)
+ return -EILSEQ;
+ if (out_strs && i >= skip)
+ *out_strs++ = p;
+ }
+ i -= skip;
+ return i <= 0 ? -ENODATA : i;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string_helper);
+
+const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
+ u32 *pu)
+{
+ const void *curv = cur;
+
+ if (!prop)
+ return NULL;
+
+ if (!cur) {
+ curv = prop->value;
+ goto out_val;
+ }
+
+ curv += sizeof(*cur);
+ if (curv >= prop->value + prop->length)
+ return NULL;
+
+out_val:
+ *pu = be32_to_cpup(curv);
+ return curv;
+}
+EXPORT_SYMBOL_GPL(of_prop_next_u32);
+
+const char *of_prop_next_string(struct property *prop, const char *cur)
+{
+ const void *curv = cur;
+
+ if (!prop)
+ return NULL;
+
+ if (!cur)
+ return prop->value;
+
+ curv += strlen(cur) + 1;
+ if (curv >= prop->value + prop->length)
+ return NULL;
+
+ return curv;
+}
+EXPORT_SYMBOL_GPL(of_prop_next_string);
+
+/**
+ * of_graph_parse_endpoint() - parse common endpoint node properties
+ * @node: pointer to endpoint device_node
+ * @endpoint: pointer to the OF endpoint data structure
+ *
+ * The caller should hold a reference to @node.
+ */
+int of_graph_parse_endpoint(const struct device_node *node,
+ struct of_endpoint *endpoint)
+{
+ struct device_node *port_node = of_get_parent(node);
+
+ WARN_ONCE(!port_node, "%s(): endpoint %s has no parent node\n",
+ __func__, node->full_name);
+
+ memset(endpoint, 0, sizeof(*endpoint));
+
+ endpoint->local_node = node;
+ /*
+ * It doesn't matter whether the two calls below succeed.
+ * If they don't then the default value 0 is used.
+ */
+ of_property_read_u32(port_node, "reg", &endpoint->port);
+ of_property_read_u32(node, "reg", &endpoint->id);
+
+ of_node_put(port_node);
+
+ return 0;
+}
+EXPORT_SYMBOL(of_graph_parse_endpoint);
+
+/**
+ * of_graph_get_port_by_id() - get the port matching a given id
+ * @parent: pointer to the parent device node
+ * @id: id of the port
+ *
+ * Return: A 'port' node pointer with refcount incremented. The caller
+ * has to use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
+{
+ struct device_node *node, *port;
+
+ node = of_get_child_by_name(parent, "ports");
+ if (node)
+ parent = node;
+
+ for_each_child_of_node(parent, port) {
+ u32 port_id = 0;
+
+ if (of_node_cmp(port->name, "port") != 0)
+ continue;
+ of_property_read_u32(port, "reg", &port_id);
+ if (id == port_id)
+ break;
+ }
+
+ of_node_put(node);
+
+ return port;
+}
+EXPORT_SYMBOL(of_graph_get_port_by_id);
+
+/**
+ * of_graph_get_next_endpoint() - get next endpoint node
+ * @parent: pointer to the parent device node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
+ struct device_node *prev)
+{
+ struct device_node *endpoint;
+ struct device_node *port;
+
+ if (!parent)
+ return NULL;
+
+ /*
+ * Start by locating the port node. If no previous endpoint is specified
+ * search for the first port node, otherwise get the previous endpoint
+ * parent port node.
+ */
+ if (!prev) {
+ struct device_node *node;
+
+ node = of_get_child_by_name(parent, "ports");
+ if (node)
+ parent = node;
+
+ port = of_get_child_by_name(parent, "port");
+ of_node_put(node);
+
+ if (!port) {
+ pr_err("graph: no port node found in %s\n",
+ parent->full_name);
+ return NULL;
+ }
+ } else {
+ port = of_get_parent(prev);
+ if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
+ __func__, prev->full_name))
+ return NULL;
+ }
+
+ while (1) {
+ /*
+ * Now that we have a port node, get the next endpoint by
+ * getting the next child. If the previous endpoint is NULL this
+ * will return the first child.
+ */
+ endpoint = of_get_next_child(port, prev);
+ if (endpoint) {
+ of_node_put(port);
+ return endpoint;
+ }
+
+ /* No more endpoints under this port, try the next one. */
+ prev = NULL;
+
+ do {
+ port = of_get_next_child(parent, port);
+ if (!port)
+ return NULL;
+ } while (of_node_cmp(port->name, "port"));
+ }
+}
+EXPORT_SYMBOL(of_graph_get_next_endpoint);
+
+/**
+ * of_graph_get_endpoint_by_regs() - get endpoint node of specific identifiers
+ * @parent: pointer to the parent device node
+ * @port_reg: identifier (value of reg property) of the parent port node
+ * @reg: identifier (value of reg property) of the endpoint node
+ *
+ * Return: An 'endpoint' node pointer which is identified by reg and at the same
+ * is the child of a port node identified by port_reg. reg and port_reg are
+ * ignored when they are -1.
+ */
+struct device_node *of_graph_get_endpoint_by_regs(
+ const struct device_node *parent, int port_reg, int reg)
+{
+ struct of_endpoint endpoint;
+ struct device_node *node = NULL;
+
+ for_each_endpoint_of_node(parent, node) {
+ of_graph_parse_endpoint(node, &endpoint);
+ if (((port_reg == -1) || (endpoint.port == port_reg)) &&
+ ((reg == -1) || (endpoint.id == reg)))
+ return node;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
+
+/**
+ * of_graph_get_remote_port_parent() - get remote port's parent node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ * to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_port_parent(
+ const struct device_node *node)
+{
+ struct device_node *np;
+ unsigned int depth;
+
+ /* Get remote endpoint node. */
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+
+ /* Walk 3 levels up only if there is 'ports' node. */
+ for (depth = 3; depth && np; depth--) {
+ np = of_get_next_parent(np);
+ if (depth == 2 && of_node_cmp(np->name, "ports"))
+ break;
+ }
+ return np;
+}
+EXPORT_SYMBOL(of_graph_get_remote_port_parent);
+
+/**
+ * of_graph_get_remote_port() - get remote port node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote port node associated with remote endpoint node linked
+ * to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_port(const struct device_node *node)
+{
+ struct device_node *np;
+
+ /* Get remote endpoint node. */
+ np = of_parse_phandle(node, "remote-endpoint", 0);
+ if (!np)
+ return NULL;
+ return of_get_next_parent(np);
+}
+EXPORT_SYMBOL(of_graph_get_remote_port);
+
+/**
+ * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
+ * @node: pointer to parent device_node containing graph port/endpoint
+ * @port: identifier (value of reg property) of the parent port node
+ * @endpoint: identifier (value of reg property) of the endpoint node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ * to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_node(const struct device_node *node,
+ u32 port, u32 endpoint)
+{
+ struct device_node *endpoint_node, *remote;
+
+ endpoint_node = of_graph_get_endpoint_by_regs(node, port, endpoint);
+ if (!endpoint_node) {
+ pr_debug("no valid endpoint (%d, %d) for node %s\n",
+ port, endpoint, node->full_name);
+ return NULL;
+ }
+
+ remote = of_graph_get_remote_port_parent(endpoint_node);
+ of_node_put(endpoint_node);
+ if (!remote) {
+ pr_debug("no valid remote node\n");
+ return NULL;
+ }
+
+ if (!of_device_is_available(remote)) {
+ pr_debug("not available for remote node\n");
+ return NULL;
+ }
+
+ return remote;
+}
+EXPORT_SYMBOL(of_graph_get_remote_node);
--
2.7.4
^ permalink raw reply related
* [PATCH v5 5/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-veyron
From: Guillaume Tucker @ 2017-05-03 9:56 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
devicetree, linux-rockchip, linux-arm-kernel, linux-kernel,
Guillaume Tucker
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>
From: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Add reference to the Mali GPU device tree node on rk3288-veyron.
Tested on Minnie and Jerry boards.
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
arch/arm/boot/dts/rk3288-veyron.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi
index 5d1eb0a25827..9847d5c6db3b 100644
--- a/arch/arm/boot/dts/rk3288-veyron.dtsi
+++ b/arch/arm/boot/dts/rk3288-veyron.dtsi
@@ -447,6 +447,11 @@
status = "okay";
};
+&gpu {
+ mali-supply = <&vdd_gpu>;
+ status = "okay";
+};
+
&wdt {
status = "okay";
};
--
2.11.0
^ permalink raw reply related
* [PATCH v5 4/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-firefly
From: Guillaume Tucker @ 2017-05-03 9:56 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
linux-rockchip, John Reitan, Enric Balletbo i Serra,
linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>
Add reference to the Mali GPU device tree node on rk3288-firefly.
Tested on Firefly board.
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
arch/arm/boot/dts/rk3288-firefly.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 10793ac18599..f520589493b4 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -594,3 +594,8 @@
&wdt {
status = "okay";
};
+
+&gpu {
+ mali-supply = <&vdd_gpu>;
+ status = "okay";
+};
--
2.11.0
^ permalink raw reply related
* [PATCH v5 3/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-rock2-som
From: Guillaume Tucker @ 2017-05-03 9:56 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
linux-rockchip, John Reitan, Enric Balletbo i Serra,
linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>
Add reference to the Mali GPU device tree node on the
rk3288-rock2-som platform. Tested on a Radxa Rock2 Square board.
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
arch/arm/boot/dts/rk3288-rock2-som.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288-rock2-som.dtsi b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
index 1c0bbc9b928b..f694867fa46a 100644
--- a/arch/arm/boot/dts/rk3288-rock2-som.dtsi
+++ b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
@@ -301,3 +301,8 @@
&wdt {
status = "okay";
};
+
+&gpu {
+ mali-supply = <&vdd_gpu>;
+ status = "okay";
+};
--
2.11.0
^ permalink raw reply related
* [PATCH v5 2/5] ARM: dts: rockchip: add ARM Mali GPU node for rk3288
From: Guillaume Tucker @ 2017-05-03 9:56 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
linux-rockchip, John Reitan, Enric Balletbo i Serra,
linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>
Add Mali GPU device tree node for the rk3288 SoC, with devfreq
opp table.
Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
arch/arm/boot/dts/rk3288.dtsi | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index df8a0dbe9d91..35969041eae2 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -43,6 +43,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/clock/rk3288-cru.h>
+#include <dt-bindings/power/rk3288-power.h>
#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/power/rk3288-power.h>
#include <dt-bindings/soc/rockchip,boot-mode.h>
@@ -1117,6 +1118,48 @@
};
};
+ gpu: mali@ffa30000 {
+ compatible = "rockchip,rk3288-mali", "arm,mali-t760", "arm,mali-midgard";
+ reg = <0xffa30000 0x10000>;
+ interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "job", "mmu", "gpu";
+ clocks = <&cru ACLK_GPU>;
+ operating-points-v2 = <&gpu_opp_table>;
+ power-domains = <&power RK3288_PD_GPU>;
+ status = "disabled";
+ };
+
+ gpu_opp_table: opp_table0 {
+ compatible = "operating-points-v2";
+
+ opp@100000000 {
+ opp-hz = /bits/ 64 <100000000>;
+ opp-microvolt = <950000>;
+ };
+ opp@200000000 {
+ opp-hz = /bits/ 64 <200000000>;
+ opp-microvolt = <950000>;
+ };
+ opp@300000000 {
+ opp-hz = /bits/ 64 <300000000>;
+ opp-microvolt = <1000000>;
+ };
+ opp@400000000 {
+ opp-hz = /bits/ 64 <400000000>;
+ opp-microvolt = <1100000>;
+ };
+ opp@500000000 {
+ opp-hz = /bits/ 64 <500000000>;
+ opp-microvolt = <1200000>;
+ };
+ opp@600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <1250000>;
+ };
+ };
+
qos_gpu_r: qos@ffaa0000 {
compatible = "syscon";
reg = <0xffaa0000 0x20>;
--
2.11.0
^ permalink raw reply related
* [PATCH v5 1/5] dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
From: Guillaume Tucker @ 2017-05-03 9:56 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
devicetree, linux-rockchip, linux-arm-kernel, linux-kernel,
Guillaume Tucker
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>
The ARM Mali Midgard GPU family is present in a number of SoCs
from many different vendors such as Samsung Exynos and Rockchip.
Import the device tree bindings documentation from the r16p0
release of the Mali Midgard GPU kernel driver:
https://developer.arm.com/-/media/Files/downloads/mali-drivers/kernel/mali-midgard-gpu/TX011-SW-99002-r16p0-00rel0.tgz
Remove the copyright and GPL licence header as deemed not necessary.
Redesign the "compatible" property strings to list all the Mali
Midgard GPU types and add vendor specific ones.
Drop the "clock-names" property as the Mali Midgard GPU uses only one
clock (the driver now needs to call clk_get with NULL).
Convert the "interrupt-names" property values to lower-case: "job",
"mmu" and "gpu".
Replace the deprecated "operating-points" optional property with
"operating-points-v2".
Omit the following optional properties in this initial version as they
are only used in very specific cases:
* snoop_enable_smc
* snoop_disable_smc
* jm_config
* power_model
* system-coherency
* ipa-model
Update the example accordingly to reflect all these changes, based on
rk3288 mali-t760.
CC: John Reitan <john.reitan@arm.com>
Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
.../devicetree/bindings/gpu/arm,mali-midgard.txt | 86 ++++++++++++++++++++++
1 file changed, 86 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
new file mode 100644
index 000000000000..d3b6e1a4713a
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
@@ -0,0 +1,86 @@
+ARM Mali Midgard GPU
+====================
+
+Required properties:
+
+- compatible :
+ * Must contain one of the following:
+ + "arm,mali-t604"
+ + "arm,mali-t624"
+ + "arm,mali-t628"
+ + "arm,mali-t720"
+ + "arm,mali-t760"
+ + "arm,mali-t820"
+ + "arm,mali-t830"
+ + "arm,mali-t860"
+ + "arm,mali-t880"
+ * which must be preceded by one of the following vendor specifics:
+ + "amlogic,meson-gxm-mali"
+ + "rockchip,rk3288-mali"
+
+- reg : Physical base address of the device and length of the register area.
+
+- interrupts : Contains the three IRQ lines required by Mali Midgard devices.
+
+- interrupt-names : Contains the names of IRQ resources in the order they were
+ provided in the interrupts property. Must contain: "job", "mmu", "gpu".
+
+
+Optional properties:
+
+- clocks : Phandle to clock for the Mali Midgard device.
+
+- mali-supply : Phandle to regulator for the Mali device. Refer to
+ Documentation/devicetree/bindings/regulator/regulator.txt for details.
+
+- operating-points-v2 : Refer to Documentation/devicetree/bindings/power/opp.txt
+ for details.
+
+
+Example for a Mali-T760:
+
+gpu@ffa30000 {
+ compatible = "rockchip,rk3288-mali", "arm,mali-t760", "arm,mali-midgard";
+ reg = <0xffa30000 0x10000>;
+ interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "job", "mmu", "gpu";
+ clocks = <&cru ACLK_GPU>;
+ mali-supply = <&vdd_gpu>;
+ operating-points-v2 = <&gpu_opp_table>;
+ power-domains = <&power RK3288_PD_GPU>;
+};
+
+gpu_opp_table: opp_table0 {
+ compatible = "operating-points-v2";
+
+ opp@533000000 {
+ opp-hz = /bits/ 64 <533000000>;
+ opp-microvolt = <1250000>;
+ };
+ opp@450000000 {
+ opp-hz = /bits/ 64 <450000000>;
+ opp-microvolt = <1150000>;
+ };
+ opp@400000000 {
+ opp-hz = /bits/ 64 <400000000>;
+ opp-microvolt = <1125000>;
+ };
+ opp@350000000 {
+ opp-hz = /bits/ 64 <350000000>;
+ opp-microvolt = <1075000>;
+ };
+ opp@266000000 {
+ opp-hz = /bits/ 64 <266000000>;
+ opp-microvolt = <1025000>;
+ };
+ opp@160000000 {
+ opp-hz = /bits/ 64 <160000000>;
+ opp-microvolt = <925000>;
+ };
+ opp@100000000 {
+ opp-hz = /bits/ 64 <100000000>;
+ opp-microvolt = <912500>;
+ };
+};
--
2.11.0
^ permalink raw reply related
* [PATCH v5 0/5] Add ARM Mali Midgard device tree bindings and gpu node for rk3288
From: Guillaume Tucker @ 2017-05-03 9:56 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Guillaume Tucker
The ARM Mali Midgard GPU kernel driver is only available
out-of-tree and is not going to be merged in its current form.
However, it would be useful to have its device tree bindings
merged. In particular, this would enable distributions to create
working driver packages (dkms...) without having to patch the
kernel.
The bindings for the earlier Mali Utgard GPU family have already
been merged, so this is essentially the same scenario but for
newer GPUs (Mali-T604 ~ Mali-T880).
This series of patches first imports the bindings from the latest
driver release with some clean-up then adds a gpu node for the
rk3288 SoC. This was successfully tested on Radxa Rock2 Square,
Firefly, Veyron Minnie and Jerry boards using Mali kernel driver
r16p0 and r12p0 user-space binary.
Changes since v1:
- enabled gpu on rk3288-veyron boards
Changes since v2:
- removed "clk-names" property and "clk_mali" name
- converted values of "interrupt-names" property to
lower-case: "job", "mmu" and "gpu"
- replaced dt compatible strings with list of all Midgard GPU variants and
optional vendors
- cleaned up gpu node example
Changes since v3:
- add "rockchip,rk3288-mali" vendor compatible string
- move gpu node at the right location in rk3288.dtsi
- use operating-points-v2 in documentation and rk3288.dtsi
Changes since v4:
- removed wildcards (mali-t60x, -t62x)
- vendor compatible strings are not optional any more
- example updated accordingly, based on rk3288
Enric Balletbo i Serra (1):
ARM: dts: rockchip: enable ARM Mali GPU on rk3288-veyron
Guillaume Tucker (4):
dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
ARM: dts: rockchip: add ARM Mali GPU node for rk3288
ARM: dts: rockchip: enable ARM Mali GPU on rk3288-rock2-som
ARM: dts: rockchip: enable ARM Mali GPU on rk3288-firefly
.../devicetree/bindings/gpu/arm,mali-midgard.txt | 86 ++++++++++++++++++++++
arch/arm/boot/dts/rk3288-firefly.dtsi | 5 ++
arch/arm/boot/dts/rk3288-rock2-som.dtsi | 5 ++
arch/arm/boot/dts/rk3288-veyron.dtsi | 5 ++
arch/arm/boot/dts/rk3288.dtsi | 43 +++++++++++
5 files changed, 144 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
--
2.11.0
--
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 2/4] soc: qcom: Introduce APCS IPC driver
From: Jassi Brar @ 2017-05-03 9:55 UTC (permalink / raw)
To: Loic PALLARDY
Cc: Bjorn Andersson, Andy Gross, Rob Herring, Mark Rutland,
Ohad Ben-Cohen,
linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <c9b86ecfa6b84ae994da6c30d2840bbf-GGltqRL9kSEdIAuSVraBWEEOCMrvLtNR@public.gmane.org>
Loic, thanks for adding me.
On Wed, May 3, 2017 at 2:58 PM, Loic PALLARDY <loic.pallardy-qxv4g6HH51o@public.gmane.org> wrote:
>
>
>> -----Original Message-----
>> From: linux-remoteproc-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-remoteproc-
>> owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Bjorn Andersson
>> Sent: Wednesday, May 03, 2017 7:29 AM
>> To: Andy Gross <andy.gross-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>; Rob Herring
>> <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>; Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>; Ohad Ben-
>> Cohen <ohad-Ix1uc/W3ht7QT0dZR+AlfA@public.gmane.org>
>> Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
>> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-
>> remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> Subject: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
>>
>> This implements a driver that exposes the IPC bits found in the APCS Global
>> block in various Qualcomm platforms. The bits are used to signal inter-
>> processor communication signals from the application CPU to other masters.
>>
>> The driver implements the "doorbell" binding and could be used as basis for a
>> new Linux framework, if found useful outside Qualcomm.
>>
> Hi Bjorn,
>
> Even if Qualcom APCS IPC is limited, why don't you rely on existing mailbox framework.
> It is there to gather all IPC management under the same interface.
> No need to create a new one from my pov.
> If you don't provide message data, mailbox framework behaves as doorbell.
>
QCOM RPM reinvented the wheel for what mailbox framework already did,
despite my pointing it out =>
http://lkml.iu.edu/hypermail//linux/kernel/1406.2/03918.html
The driver bypassed mailbox framework and was pushed via another tree.
Same is being attempted now, only now it is more expensive to switch
to generic mailbox framework having spent so much time on QCOM
specific implementation of controller and protocol drivers inside
drivers/soc/qcom/
--
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/4] net: macb: Add hardware PTP support
From: Richard Cochran @ 2017-05-03 9:43 UTC (permalink / raw)
To: Rafal Ozieblo
Cc: David Miller,
nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org,
netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
harinikatakamlinux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
harini.katakam-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org,
Andrei.Pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org
In-Reply-To: <BN3PR07MB2516757CB4EA7367F6623C06C9170-EldUQEzkDQfxGZiqM5fOI+FPX92sqiQdvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>
On Tue, May 02, 2017 at 01:57:15PM +0000, Rafal Ozieblo wrote:
> > What is the point of this wrapper function anyhow? Please remove it.
> gem_ptp_gettime() is assigned in ptp_clock_info and it has to have
> ptp_clock_info pointer as first parameter. gem_tsu_get_time() is used in
> the source code but with macb pointer.
> Do you want me to do something like:
> gem_ptp_gettime(macb->ptp, ts);
> and first would be getting macb pointer from ptp ?
> struct macb *bp = container_of(ptp, struct macb, ptp_clock_info);
Yes. Unless your sub-function is used in more than one place, then it
is wasteful and confusing to wrap the functionality for no apparent
reason.
> > > + switch (rq->type) {
> > > + case PTP_CLK_REQ_EXTTS: /* Toggle TSU match interrupt */
> > > + if (on)
> > > + macb_writel(bp, IER, MACB_BIT(TCI));
> >
> > No locking to protect IER and IDE?
> There is no need.
But what happens when the PTP_CLK_REQ_EXTTS and PTP_CLK_REQ_PPS ioctls
are called at the same time?
You need to ensure that IDR is consistent. If the bits are write
only, then you should comment this fact.
> > > + else
> > > + macb_writel(bp, IDR, MACB_BIT(TCI));
> > > + break;
> > > + case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */
> > > + return -EOPNOTSUPP;
> > > + /* break; */
> > > + case PTP_CLK_REQ_PPS: /* Toggle TSU periodic (second)
> > interrupt */
> > > + if (on)
> > > + macb_writel(bp, IER, MACB_BIT(SRI));
> > > + else
> > > + macb_writel(bp, IDR, MACB_BIT(SRI));
> > > + break;
> > > + default:
> > > + break;
> > > + }
> > > + return 0;
> > > +}
Thanks,
Richard
--
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 2/4] soc: qcom: Introduce APCS IPC driver
From: Loic PALLARDY @ 2017-05-03 9:28 UTC (permalink / raw)
To: Bjorn Andersson, Andy Gross, Rob Herring, Mark Rutland,
Ohad Ben-Cohen,
jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20170503052929.17422-2-bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> -----Original Message-----
> From: linux-remoteproc-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-remoteproc-
> owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Bjorn Andersson
> Sent: Wednesday, May 03, 2017 7:29 AM
> To: Andy Gross <andy.gross-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>; Rob Herring
> <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>; Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>; Ohad Ben-
> Cohen <ohad-Ix1uc/W3ht7QT0dZR+AlfA@public.gmane.org>
> Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-
> remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Subject: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
>
> This implements a driver that exposes the IPC bits found in the APCS Global
> block in various Qualcomm platforms. The bits are used to signal inter-
> processor communication signals from the application CPU to other masters.
>
> The driver implements the "doorbell" binding and could be used as basis for a
> new Linux framework, if found useful outside Qualcomm.
>
Hi Bjorn,
Even if Qualcom APCS IPC is limited, why don't you rely on existing mailbox framework.
It is there to gather all IPC management under the same interface.
No need to create a new one from my pov.
If you don't provide message data, mailbox framework behaves as doorbell.
Regards,
Loic
> Signed-off-by: Bjorn Andersson <bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>
> Changes since v2:
> - New driver
>
> drivers/soc/qcom/Kconfig | 8 ++
> drivers/soc/qcom/Makefile | 1 +
> drivers/soc/qcom/apcs-ipc.c | 182
> ++++++++++++++++++++++++++++++++++++++
> include/linux/soc/qcom/apcs_ipc.h | 26 ++++++
> 4 files changed, 217 insertions(+)
> create mode 100644 drivers/soc/qcom/apcs-ipc.c create mode 100644
> include/linux/soc/qcom/apcs_ipc.h
>
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index
> 78b1bb7bcf20..4113da81d18b 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -1,6 +1,14 @@
> #
> # QCOM Soc drivers
> #
> +config QCOM_APCS_IPC
> + tristate "Qualcomm APCS IPC driver"
> + depends on ARCH_QCOM
> + help
> + Say y here to enable support for the APCS IPC doorbell driver,
> + providing an interface for invoking the inter-process communication
> + signals from the application processor to other masters.
> +
> config QCOM_GSBI
> tristate "QCOM General Serial Bus Interface"
> depends on ARCH_QCOM
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index
> 1f30260b06b8..e15b33e5a630 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,3 +1,4 @@
> +obj-$(CONFIG_QCOM_APCS_IPC) += apcs-ipc.o
> obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
> obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
> obj-$(CONFIG_QCOM_PM) += spm.o
> diff --git a/drivers/soc/qcom/apcs-ipc.c b/drivers/soc/qcom/apcs-ipc.c new
> file mode 100644 index 000000000000..ea835cb08657
> --- /dev/null
> +++ b/drivers/soc/qcom/apcs-ipc.c
> @@ -0,0 +1,182 @@
> +/*
> + * Copyright (c) 2017, Linaro Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +static struct platform_driver qcom_apcs_ipc_driver;
> +
> +struct qcom_apcs_ipc {
> + struct device *dev;
> +
> + void __iomem *base;
> + unsigned long offset;
> +};
> +
> +struct qcom_apcs_ipc_bell {
> + struct qcom_apcs_ipc *apcs;
> + unsigned int bit;
> +};
> +
> +static void qcom_apcs_ipc_release(struct device *dev, void *res) {
> + struct qcom_apcs_ipc_bell *bell = res;
> + struct qcom_apcs_ipc *apcs = bell->apcs;
> +
> + put_device(apcs->dev);
> +}
> +
> +/**
> + * qcom_apcs_ipc_get() - acquire a handle to a doorbell
> + * @dev: client device handle
> + * @id: identifier of the doorbell
> + *
> + * Returns a doorbell reference, or negative errno on failure.
> + */
> +struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct device *dev,
> + const char *id)
> +{
> + struct qcom_apcs_ipc_bell *bell;
> + struct platform_device *pdev;
> + struct of_phandle_args args;
> + int index = 0;
> + int ret;
> +
> + if (id) {
> + index = of_property_match_string(dev->of_node,
> + "doorbell-names", id);
> + if (index < 0)
> + return ERR_PTR(index);
> + }
> +
> + ret = of_parse_phandle_with_args(dev->of_node, "doorbells",
> + "#doorbell-cells", index, &args);
> + if (ret) {
> + dev_err(dev, "unable to resolve doorbell\n");
> + return ERR_PTR(-ENODEV);
> + }
> +
> + pdev = of_find_device_by_node(args.np);
> + of_node_put(args.np);
> +
> + if (!pdev)
> + return ERR_PTR(-EPROBE_DEFER);
> +
> + if (args.args[0] >= 32) {
> + dev_err(dev, "invalid doorbell requested\n");
> + ret = -EINVAL;
> + goto release_device;
> + }
> +
> + if (pdev->dev.driver != &qcom_apcs_ipc_driver.driver) {
> + dev_err(dev, "failed to acquire apcs ipc driver\n");
> + ret = -EINVAL;
> + goto release_device;
> + }
> +
> + bell = devres_alloc(qcom_apcs_ipc_release, sizeof(*bell),
> GFP_KERNEL);
> + if (!bell) {
> + ret = -ENOMEM;
> + goto release_device;
> + }
> +
> + bell->apcs = platform_get_drvdata(pdev);
> + bell->bit = args.args[0];
> +
> + devres_add(dev, bell);
> +
> + return bell;
> +
> +release_device:
> + put_device(&pdev->dev);
> +
> + return ERR_PTR(ret);
> +
> +}
> +EXPORT_SYMBOL_GPL(devm_qcom_apcs_ipc_get);
> +
> +/**
> + * qcom_apcs_ipc_ring() - ring the doorbell
> + * @bell: doorbell to ring
> + */
> +void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell) {
> + struct qcom_apcs_ipc *apcs = bell->apcs;
> +
> + writel(BIT(bell->bit), apcs->base + apcs->offset); }
> +EXPORT_SYMBOL_GPL(qcom_apcs_ipc_ring);
> +
> +static int qcom_apcs_ipc_probe(struct platform_device *pdev) {
> + struct qcom_apcs_ipc *apcs;
> + struct resource *res;
> +
> + apcs = devm_kzalloc(&pdev->dev, sizeof(*apcs), GFP_KERNEL);
> + if (!apcs)
> + return -ENOMEM;
> +
> + apcs->dev = &pdev->dev;
> + apcs->offset = (unsigned long)of_device_get_match_data(&pdev-
> >dev);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + apcs->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(apcs->base))
> + return PTR_ERR(apcs->base);
> +
> + platform_set_drvdata(pdev, apcs);
> +
> + return 0;
> +}
> +
> +static int qcom_apcs_ipc_remove(struct platform_device *pdev) {
> + return 0;
> +}
> +
> +/* .data is the offset of the ipc register within the global block */
> +static const struct of_device_id qcom_apcs_ipc_of_match[] = {
> + { .compatible = "qcom,msm8916-apcs-kpss-global", .data = (void *)8
> },
> + { .compatible = "qcom,msm8996-apcs-hmss-global", .data = (void
> *)16 },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match);
> +
> +static struct platform_driver qcom_apcs_ipc_driver = {
> + .probe = qcom_apcs_ipc_probe,
> + .remove = qcom_apcs_ipc_remove,
> + .driver = {
> + .name = "qcom_apcs_ipc",
> + .of_match_table = qcom_apcs_ipc_of_match,
> + },
> +};
> +
> +static int __init qcom_apcs_ipc_init(void) {
> + return platform_driver_register(&qcom_apcs_ipc_driver);
> +}
> +postcore_initcall(qcom_apcs_ipc_init);
> +
> +static void __exit qcom_apcs_ipc_exit(void) {
> + platform_driver_unregister(&qcom_apcs_ipc_driver);
> +}
> +module_exit(qcom_apcs_ipc_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Qualcomm APCS IPC driver");
> diff --git a/include/linux/soc/qcom/apcs_ipc.h
> b/include/linux/soc/qcom/apcs_ipc.h
> new file mode 100644
> index 000000000000..72be77555261
> --- /dev/null
> +++ b/include/linux/soc/qcom/apcs_ipc.h
> @@ -0,0 +1,26 @@
> +#ifndef __QCOM_APCS_IPC_H__
> +#define __QCOM_APCS_IPC_H__
> +
> +#include <linux/err.h>
> +
> +struct device;
> +struct qcom_apcs_ipc_bell;
> +
> +#if IS_ENABLED(CONFIG_QCOM_APCS_IPC)
> +
> +struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct device *dev,
> + const char *id);
> +void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell);
> +
> +#else
> +
> +static inline struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct
> device *dev,
> + const char
> *id)
> +{
> + return ERR_PTR(-EINVAL);
> +}
> +
> +static inline void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell)
> +{}
> +
> +#endif
> +#endif
> --
> 2.12.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-remoteproc" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo
> info at http://vger.kernel.org/majordomo-info.html
--
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: [PATCHv2 2/2] iio: adc: add driver for the ti-adc084s021 chip
From: Mårten Lindahl @ 2017-05-03 9:13 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Mårten Lindahl, knaack.h-Mmb7MZpHnFY,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
linux-iio-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <88d6c17d-8eae-6a41-b304-5224a7a2194a-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On Sun, 2017-04-30 at 16:51 +0100, Jonathan Cameron wrote:
> On 30/04/17 13:53, Mårten Lindahl wrote:
> > From: Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>
> >
> > This adds support for the Texas Instruments ADC084S021 ADC chip.
> >
> > Signed-off-by: Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>
> A few more bits inline. Mostly stuff that has come up
> in the V2 changes (and the inevitable bits I missed the
> first time!)
>
> Jonathan
Hi Jonathan! Please see my comments. I will send v3 today.
Thanks,
Mårten
> > ---
> > Changes in v2:
> > - Removed most #defines in favor of inlines
> > - Corrected channel macro
> > - Removed configuration array with only one item
> > - Updated func adc084s021_adc_conversion to use be16_to_cpu
> > - Added IIO_CHAN_INFO_SCALE to func adc084s021_read_raw
> > - Use iio_device_claim_direct_mode in func adc084s021_read_raw
> > - Removed documentation for standard driver functions
> > - Changed retval to ret everywhere
> > - Removed dynamic alloc for data buffer in trigger handler
> > - Keeping mutex for all iterations in trigger handler
> > - Removed usage of events in this driver
> > - Removed info log in probe
> > - Use spi_message_init_with_transfers for spi message structs
> > - Use preenable and postdisable functions for regulator
> > - Inserted blank line before last return in all functions
> >
> > drivers/iio/adc/Kconfig | 12 ++
> > drivers/iio/adc/Makefile | 1 +
> > drivers/iio/adc/ti-adc084s021.c | 294 ++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 307 insertions(+)
> > create mode 100644 drivers/iio/adc/ti-adc084s021.c
> >
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index dedae7a..13141e5 100644
> > --- a/drivers/iio/adc/Kconfig
> > +++ b/drivers/iio/adc/Kconfig
> > @@ -560,6 +560,18 @@ config TI_ADC0832
> > This driver can also be built as a module. If so, the module will be
> > called ti-adc0832.
> >
> > +config TI_ADC084S021
> > + tristate "Texas Instruments ADC084S021"
> > + depends on SPI
> > + select IIO_BUFFER
> > + select IIO_TRIGGERED_BUFFER
> > + help
> > + If you say yes here you get support for Texas Instruments ADC084S021
> > + chips.
> > +
> > + This driver can also be built as a module. If so, the module will be
> > + called ti-adc084s021.
> > +
> > config TI_ADC12138
> > tristate "Texas Instruments ADC12130/ADC12132/ADC12138"
> > depends on SPI
> > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> > index d001262..b1a6158 100644
> > --- a/drivers/iio/adc/Makefile
> > +++ b/drivers/iio/adc/Makefile
> > @@ -51,6 +51,7 @@ obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
> > obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> > obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> > obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> > +obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
> > obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> > obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
> > obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
> > diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
> > new file mode 100644
> > index 0000000..2dce257
> > --- /dev/null
> > +++ b/drivers/iio/adc/ti-adc084s021.c
> > @@ -0,0 +1,294 @@
> > +/**
> > + * Copyright (C) 2017 Axis Communications AB
> > + *
> > + * Driver for Texas Instruments' ADC084S021 ADC chip.
> > + * Datasheets can be found here:
> > + * http://www.ti.com/lit/ds/symlink/adc084s021.pdf
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/spi/spi.h>
> > +#include <linux/module.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/trigger_consumer.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#define ADC084S021_DRIVER_NAME "adc084s021"
> > +
> > +struct adc084s021 {
> > + struct spi_device *spi;
> > + struct spi_message message;
> > + struct spi_transfer spi_trans[2];
> > + struct regulator *reg;
> > + struct mutex lock;
> > + /*
> > + * DMA (thus cache coherency maintenance) requires the
> > + * transfer buffers to live in their own cache lines.
> > + */
> > + union {
> > + u16 tx_buf;
> > + __be16 rx_buf;
> > + } ____cacheline_aligned;
> > +};
> > +
> > +/**
> > + * Channel specification
> > + */
> Comment doesn't add much so I'd drop it.
Fixed in v3.
> > +#define ADC084S021_VOLTAGE_CHANNEL(num) \
> > + { \
> > + .type = IIO_VOLTAGE, \
> > + .channel = (num), \
> > + .address = (num) << 3, \
> This does feel a little pointless as you can just do the shift inline and
> use the channel value instead. Doesn't really matter though.
Ok, fixed in v3.
> > + .indexed = 1, \
> > + .scan_index = (num), \
> > + .scan_type = { \
> > + .sign = 'u', \
> > + .realbits = 8, \
> > + .storagebits = 16, \
> > + .shift = 4, \
> > + .endianness = IIO_BE, \
> > + }, \
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> > + }
> > +
> > +static const struct iio_chan_spec adc084s021_channels[] = {
> > + ADC084S021_VOLTAGE_CHANNEL(0),
> > + ADC084S021_VOLTAGE_CHANNEL(1),
> > + ADC084S021_VOLTAGE_CHANNEL(2),
> > + ADC084S021_VOLTAGE_CHANNEL(3),
> > + IIO_CHAN_SOFT_TIMESTAMP(4),
> > +};
> > +
> > +/**
> > + * Read an ADC channel and return its value.
> > + *
> > + * @adc: The ADC SPI data.
> > + * @channel: The IIO channel data structure.
> > + */
> > +static int adc084s021_adc_conversion(struct adc084s021 *adc,
> > + struct iio_chan_spec const *channel)
> > +{
> > + u8 value;
> > + int ret;
> > +
> > + adc->tx_buf = channel->address;
> > +
> > + /* Do the transfer */
> > + ret = spi_sync(adc->spi, &adc->message);
> > + if (ret < 0)
> > + return ret;
> > +
> > + value = (be16_to_cpu(adc->rx_buf) >> channel->scan_type.shift) & 0xff;You want to do this for the read_raw sysfs calls, but not the buffered ones
> (where this shift and mask is made userspace's problem).
Fixed in v3.
> > +
> > + dev_dbg(&adc->spi->dev, "value 0x%02X on channel %d\n",
> > + value, channel->channel);
> > +
> > + return value;
> > +}
> > +
> > +static int adc084s021_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *channel, int *val,
> > + int *val2, long mask)
> > +{
> > + struct adc084s021 *adc = iio_priv(indio_dev);
> > + int ret;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + ret = iio_device_claim_direct_mode(indio_dev);
> > + if (ret < 0)
> > + return ret;
> > +
> Unless I'm dozing off this morning, you don't have any power..
> So you'll need to enable the regulator first in this path.
Fixed in v3.
>
> Note that the runtime power management autosuspend stuff can
> be good for this as it stops the power cycling if a short burst
> of readings is taken.
I don't have much experience of the pm_runtime system, so I looked
around to the other drivers but I see very few users of it. I can
absolutely look into it if you think it should be applied to this
driver.
> > + ret = adc084s021_adc_conversion(adc, channel);
> > + iio_device_release_direct_mode(indio_dev);
> > + if (ret < 0)
> > + return ret;
> > +
> > + *val = ret;
> > +
> > + return IIO_VAL_INT;
> > + case IIO_CHAN_INFO_SCALE:
> > + ret = regulator_get_voltage(adc->reg);
> Given the regulator is currently off as far as this device is concerned
> it's possible the answer will be 0...
Fixed in v3.
> > + if (ret < 0)
> > + return ret;
> > +
> > + *val = ret / 1000;
> > +
> > + return IIO_VAL_INT;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > +/**
> > + * Read enabled ADC channels and push data to the buffer.
> > + *
> > + * @irq: The interrupt number (not used).
> > + * @pollfunc: Pointer to the poll func.
> > + */
> > +static irqreturn_t adc084s021_buffer_trigger_handler(int irq, void *pollfunc)
> > +{
> > + struct iio_poll_func *pf = pollfunc;
> > + struct iio_dev *indio_dev = pf->indio_dev;
> > + struct adc084s021 *adc = iio_priv(indio_dev);
> > + __be16 data[8] = {0}; /* 4 * 16-bit words of data + 8 bytes timestamp */
> > + int scan_index;
> > + int i = 0;
> > +
> > + mutex_lock(&adc->lock);
> > +
> > + for_each_set_bit(scan_index, indio_dev->active_scan_mask,
> > + indio_dev->masklength) {
> > + const struct iio_chan_spec *channel =
> > + &indio_dev->channels[scan_index];
> > + data[i++] = adc084s021_adc_conversion(adc, channel);
> This is clearly a rather simplistic way of handling the read outs.
> Perfectly good for an initial version (which may never get improved
> on!) but you could do the spi transfers for a set of channels much
> more efficiently.
>
> Figure 1 on the datasheet shows how the control register for the next
> read can be written in parallel with the previous read. That would
> mean each scan took N + 1 2 byte transfers rather than 2N as currently.
>
> I'm not particularly suggesting you do this, but thought I'd just
> comment on the possibility as it is a common situation with spi
> ADCs (sometimes you get a greater lag between setup and read out
> making this even fiddlier!)
>
> If you do want to do this, the trick is to do your transfer setup
> and creation stuff in preenable so that it can be customised for
> whatever channels are enabled.
Ok, Fixed in v3.
I have looked into this and I think I improved the design according to
your suggestion. Please note: According to the datasheet (section 8.3)
after each power up the ADC will read out last scanned/first channel,
which will result in a very first read of 16 bits that is ignored. Also,
the readings are setup by only one transfer segment now.
> > + }
> > +
> > + iio_push_to_buffers_with_timestamp(indio_dev, data,
> > + iio_get_time_ns(indio_dev));
> > + mutex_unlock(&adc->lock);
> > + iio_trigger_notify_done(indio_dev->trig);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int adc084s021_buffer_preenable(struct iio_dev *indio_dev)
> > +{
> > + struct adc084s021 *adc = iio_priv(indio_dev);
> > +
> > + return regulator_enable(adc->reg);
> > +}
> > +
> > +static int adc084s021_buffer_postdisable(struct iio_dev *indio_dev)
> > +{
> > + struct adc084s021 *adc = iio_priv(indio_dev);
> > +
> > + return regulator_disable(adc->reg);
> > +}
> > +
> > +static const struct iio_info adc084s021_info = {
> > + .read_raw = adc084s021_read_raw,
> > + .driver_module = THIS_MODULE,
> > +};
> > +
> > +static const struct iio_buffer_setup_ops adc084s021_buffer_setup_ops = {
> > + .preenable = adc084s021_buffer_preenable,
> > + .postenable = iio_triggered_buffer_postenable,
> > + .predisable = iio_triggered_buffer_predisable,
> > + .postdisable = adc084s021_buffer_postdisable,
> > +};
> > +
> > +static int adc084s021_probe(struct spi_device *spi)
> > +{
> > + struct iio_dev *indio_dev;
> > + struct adc084s021 *adc;
> > + int ret;
> > +
> > + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
> > + if (!indio_dev) {
> > + dev_err(&spi->dev, "Failed to allocate IIO device\n");
> > + return -ENOMEM;
> > + }
> > +
> > + adc = iio_priv(indio_dev);
> > + adc->spi = spi;
> > + spi->bits_per_word = 8;
> > +
> > + /* Update the SPI device with config and connect the iio dev */
> > + ret = spi_setup(spi);
> > + if (ret) {
> > + dev_err(&spi->dev, "Failed to update SPI device\n");
> > + return ret;
> > + }
> > + spi_set_drvdata(spi, indio_dev);
> > +
> > + /* Initiate the Industrial I/O device */
> > + indio_dev->dev.parent = &spi->dev;
> > + indio_dev->dev.of_node = spi->dev.of_node;
> > + indio_dev->name = spi_get_device_id(spi)->name;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > + indio_dev->info = &adc084s021_info;
> > + indio_dev->channels = adc084s021_channels;
> > + indio_dev->num_channels = ARRAY_SIZE(adc084s021_channels);
> > +
> > + /* Create SPI transfer for channel reads */
> > + adc->spi_trans[0].tx_buf = &adc->tx_buf;
> > + adc->spi_trans[0].len = 2;
> > + adc->spi_trans[0].speed_hz = spi->max_speed_hz;
> You don't need to set these for individual transfers unless they
> are different from how the spi bus has been set up...
>
> It's handled in __spi_validate in drivers/spi/spi.c
>
> list_for_each_entry(xfer, &message->transfers, transfer_list) {
> message->frame_length += xfer->len;
> if (!xfer->bits_per_word)
> xfer->bits_per_word = spi->bits_per_word;
>
> if (!xfer->speed_hz)
> xfer->speed_hz = spi->max_speed_hz;
> ...
>
> So it will set them spi->... in the core if you haven't overridden.
Fixed in v3.
>
> > + adc->spi_trans[0].bits_per_word = spi->bits_per_word;
> > + adc->spi_trans[1].rx_buf = &adc->rx_buf;
> > + adc->spi_trans[1].len = 2;
> > + adc->spi_trans[1].speed_hz = spi->max_speed_hz;
> > + adc->spi_trans[1].bits_per_word = spi->bits_per_word;
> > +
> > + spi_message_init_with_transfers(&adc->message, adc->spi_trans, 2);
> > +
> > + adc->reg = devm_regulator_get(&spi->dev, "vref");
> > + if (IS_ERR(adc->reg))
> > + return PTR_ERR(adc->reg);
> > +
> > + mutex_init(&adc->lock);
> > +
> > + /* Setup triggered buffer with pollfunction */
> > + ret = iio_triggered_buffer_setup(indio_dev, NULL,
> > + adc084s021_buffer_trigger_handler,
> > + &adc084s021_buffer_setup_ops);
> > + if (ret) {
> > + dev_err(&spi->dev, "Failed to setup triggered buffer\n");
> > + return ret;
> > + }
> > +
> > + ret = iio_device_register(indio_dev);
> > + if (ret) {
> > + dev_err(&spi->dev, "Failed to register IIO device\n");
> > + iio_triggered_buffer_cleanup(indio_dev);
> With devm usage this won't be needed any more.
Fixed in v3.
>
> Hmm. We should improve the error message from the core as it'll allow us
> to remove a lot of boiler plate in drivers. Ah well, a project for
> another day.
I removed this message in v3.
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int adc084s021_remove(struct spi_device *spi)
> > +{
> > + struct iio_dev *indio_dev = spi_get_drvdata(spi);
> > +
> > + iio_device_unregister(indio_dev);
> > + iio_triggered_buffer_cleanup(indio_dev);
> Now you have moved to dynamically enabling the regulator, it makes
> sense to use the devm_ versions of iio_device_register and
> iio_triggered_buffer setup.
>
> With those, you'll be able to get rid of the remove callback entirely as
> there will be nothing to do.
Fixed in v3.
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id adc084s021_of_match[] = {
> > + { .compatible = "ti,adc084s021", },
> > + {},
> > +};
> > +MODULE_DEVICE_TABLE(of, adc084s021_of_match);
> > +
> > +static const struct spi_device_id adc084s021_id[] = {
> > + { ADC084S021_DRIVER_NAME, 0},
> > + {}
> > +};
> > +MODULE_DEVICE_TABLE(spi, adc084s021_id);
> > +
> > +static struct spi_driver adc084s021_driver = {
> > + .driver = {
> > + .name = ADC084S021_DRIVER_NAME,
> > + .of_match_table = of_match_ptr(adc084s021_of_match),
> > + },
> > + .probe = adc084s021_probe,
> > + .remove = adc084s021_remove,
> > + .id_table = adc084s021_id,
> > +};
> > +module_spi_driver(adc084s021_driver);
> > +
> > +MODULE_AUTHOR("Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>");
> > +MODULE_DESCRIPTION("Texas Instruments ADC084S021");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_VERSION("1.0");
> >
>
^ permalink raw reply
* Re: [PATCH v5 03/14] pinctrl: add a pinctrl driver for the Ingenic jz47xx SoCs
From: Paul Cercueil @ 2017-05-03 9:12 UTC (permalink / raw)
To: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
Ralf Baechle
Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, james.hogan,
linux-gpio, devicetree, linux-kernel, linux-mips, linux-mmc,
linux-mtd, linux-pwm, linux-fbdev
In-Reply-To: <20170428200824.10906-4-paul@crapouillou.net>
The dependency on MFD is gone but now I notice I forgot to remove the
'select MFD_CORE'
in the Kconfig. It'd be great if you can make a quick edit when merging
this,
otherwise I'll send a v6.
- Paul
Le 2017-04-28 22:08, Paul Cercueil a écrit :
> This driver handles pin configuration and pin muxing for the
> JZ4740 and JZ4780 SoCs from Ingenic.
>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> ---
> drivers/pinctrl/Kconfig | 10 +
> drivers/pinctrl/Makefile | 1 +
> drivers/pinctrl/pinctrl-ingenic.c | 852
> ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 863 insertions(+)
> create mode 100644 drivers/pinctrl/pinctrl-ingenic.c
>
> v2: Consider it's a new patch. Completely rewritten from v1.
> v3: 'unsigned' -> 'unsigned int'
> v4: Completely rewritten from v3.
> v5: Probe child devices directly instead of using MFD framework
>
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 8f8c2af45781..82ce72fcb8e0 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -285,6 +285,16 @@ config PINCTRL_ZYNQ
> help
> This selects the pinctrl driver for Xilinx Zynq.
>
> +config PINCTRL_INGENIC
> + bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
> + default y
> + depends on MACH_INGENIC || COMPILE_TEST
> + select GENERIC_PINCONF
> + select GENERIC_PINCTRL_GROUPS
> + select GENERIC_PINMUX_FUNCTIONS
> + select REGMAP_MMIO
> + select MFD_CORE
> +
> source "drivers/pinctrl/aspeed/Kconfig"
> source "drivers/pinctrl/bcm/Kconfig"
> source "drivers/pinctrl/berlin/Kconfig"
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index a251f439626f..80f327239d4b 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_LPC18XX) += pinctrl-lpc18xx.o
> obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o
> obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
> obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o
> +obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o
>
> obj-$(CONFIG_ARCH_ASPEED) += aspeed/
> obj-y += bcm/
> diff --git a/drivers/pinctrl/pinctrl-ingenic.c
> b/drivers/pinctrl/pinctrl-ingenic.c
> new file mode 100644
> index 000000000000..d8473d929cb1
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-ingenic.c
> @@ -0,0 +1,852 @@
> +/*
> + * Ingenic SoCs pinctrl driver
> + *
> + * Copyright (c) 2017 Paul Cercueil <paul@crapouillou.net>
> + *
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#include <linux/compiler.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "core.h"
> +#include "pinconf.h"
> +#include "pinmux.h"
> +
> +#define JZ4740_GPIO_DATA 0x10
> +#define JZ4740_GPIO_PULL_DIS 0x30
> +#define JZ4740_GPIO_FUNC 0x40
> +#define JZ4740_GPIO_SELECT 0x50
> +#define JZ4740_GPIO_DIR 0x60
> +#define JZ4740_GPIO_TRIG 0x70
> +#define JZ4740_GPIO_FLAG 0x80
> +
> +#define JZ4770_GPIO_INT 0x10
> +#define JZ4770_GPIO_MSK 0x20
> +#define JZ4770_GPIO_PAT1 0x30
> +#define JZ4770_GPIO_PAT0 0x40
> +#define JZ4770_GPIO_FLAG 0x50
> +#define JZ4770_GPIO_PEN 0x70
> +
> +#define REG_SET(x) ((x) + 0x4)
> +#define REG_CLEAR(x) ((x) + 0x8)
> +
> +#define PINS_PER_GPIO_CHIP 32
> +
> +enum jz_version {
> + ID_JZ4740,
> + ID_JZ4770,
> + ID_JZ4780,
> +};
> +
> +struct ingenic_chip_info {
> + unsigned int num_chips;
> +
> + const struct group_desc *groups;
> + unsigned int num_groups;
> +
> + const struct function_desc *functions;
> + unsigned int num_functions;
> +
> + const u32 *pull_ups, *pull_downs;
> +};
> +
> +struct ingenic_pinctrl {
> + struct device *dev;
> + struct regmap *map;
> + struct pinctrl_dev *pctl;
> + struct pinctrl_pin_desc *pdesc;
> + enum jz_version version;
> +
> + const struct ingenic_chip_info *info;
> +};
> +
> +static const u32 jz4740_pull_ups[4] = {
> + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
> +};
> +
> +static const u32 jz4740_pull_downs[4] = {
> + 0x00000000, 0x00000000, 0x00000000, 0x00000000,
> +};
> +
> +static int jz4740_mmc_1bit_pins[] = { 0x69, 0x68, 0x6a, };
> +static int jz4740_mmc_4bit_pins[] = { 0x6b, 0x6c, 0x6d, };
> +static int jz4740_uart0_data_pins[] = { 0x7a, 0x79, };
> +static int jz4740_uart0_hwflow_pins[] = { 0x7e, 0x7f, };
> +static int jz4740_uart1_data_pins[] = { 0x7e, 0x7f, };
> +static int jz4740_lcd_8bit_pins[] = {
> + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x52, 0x53, 0x54,
> +};
> +static int jz4740_lcd_16bit_pins[] = {
> + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x55,
> +};
> +static int jz4740_lcd_18bit_pins[] = { 0x50, 0x51, };
> +static int jz4740_lcd_18bit_tft_pins[] = { 0x56, 0x57, 0x31, 0x32, };
> +static int jz4740_nand_cs1_pins[] = { 0x39, };
> +static int jz4740_nand_cs2_pins[] = { 0x3a, };
> +static int jz4740_nand_cs3_pins[] = { 0x3b, };
> +static int jz4740_nand_cs4_pins[] = { 0x3c, };
> +static int jz4740_pwm_pwm0_pins[] = { 0x77, };
> +static int jz4740_pwm_pwm1_pins[] = { 0x78, };
> +static int jz4740_pwm_pwm2_pins[] = { 0x79, };
> +static int jz4740_pwm_pwm3_pins[] = { 0x7a, };
> +static int jz4740_pwm_pwm4_pins[] = { 0x7b, };
> +static int jz4740_pwm_pwm5_pins[] = { 0x7c, };
> +static int jz4740_pwm_pwm6_pins[] = { 0x7e, };
> +static int jz4740_pwm_pwm7_pins[] = { 0x7f, };
> +
> +static int jz4740_mmc_1bit_funcs[] = { 0, 0, 0, };
> +static int jz4740_mmc_4bit_funcs[] = { 0, 0, 0, };
> +static int jz4740_uart0_data_funcs[] = { 1, 1, };
> +static int jz4740_uart0_hwflow_funcs[] = { 1, 1, };
> +static int jz4740_uart1_data_funcs[] = { 2, 2, };
> +static int jz4740_lcd_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> 0, };
> +static int jz4740_lcd_16bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, };
> +static int jz4740_lcd_18bit_funcs[] = { 0, 0, };
> +static int jz4740_lcd_18bit_tft_funcs[] = { 0, 0, 0, 0, };
> +static int jz4740_nand_cs1_funcs[] = { 0, };
> +static int jz4740_nand_cs2_funcs[] = { 0, };
> +static int jz4740_nand_cs3_funcs[] = { 0, };
> +static int jz4740_nand_cs4_funcs[] = { 0, };
> +static int jz4740_pwm_pwm0_funcs[] = { 0, };
> +static int jz4740_pwm_pwm1_funcs[] = { 0, };
> +static int jz4740_pwm_pwm2_funcs[] = { 0, };
> +static int jz4740_pwm_pwm3_funcs[] = { 0, };
> +static int jz4740_pwm_pwm4_funcs[] = { 0, };
> +static int jz4740_pwm_pwm5_funcs[] = { 0, };
> +static int jz4740_pwm_pwm6_funcs[] = { 0, };
> +static int jz4740_pwm_pwm7_funcs[] = { 0, };
> +
> +#define INGENIC_PIN_GROUP(name, id) \
> + { \
> + name, \
> + id##_pins, \
> + ARRAY_SIZE(id##_pins), \
> + id##_funcs, \
> + }
> +
> +static const struct group_desc jz4740_groups[] = {
> + INGENIC_PIN_GROUP("mmc-1bit", jz4740_mmc_1bit),
> + INGENIC_PIN_GROUP("mmc-4bit", jz4740_mmc_4bit),
> + INGENIC_PIN_GROUP("uart0-data", jz4740_uart0_data),
> + INGENIC_PIN_GROUP("uart0-hwflow", jz4740_uart0_hwflow),
> + INGENIC_PIN_GROUP("uart1-data", jz4740_uart1_data),
> + INGENIC_PIN_GROUP("lcd-8bit", jz4740_lcd_8bit),
> + INGENIC_PIN_GROUP("lcd-16bit", jz4740_lcd_16bit),
> + INGENIC_PIN_GROUP("lcd-18bit", jz4740_lcd_18bit),
> + INGENIC_PIN_GROUP("lcd-18bit-tft", jz4740_lcd_18bit_tft),
> + { "lcd-no-pins", },
> + INGENIC_PIN_GROUP("nand-cs1", jz4740_nand_cs1),
> + INGENIC_PIN_GROUP("nand-cs2", jz4740_nand_cs2),
> + INGENIC_PIN_GROUP("nand-cs3", jz4740_nand_cs3),
> + INGENIC_PIN_GROUP("nand-cs4", jz4740_nand_cs4),
> + INGENIC_PIN_GROUP("pwm0", jz4740_pwm_pwm0),
> + INGENIC_PIN_GROUP("pwm1", jz4740_pwm_pwm1),
> + INGENIC_PIN_GROUP("pwm2", jz4740_pwm_pwm2),
> + INGENIC_PIN_GROUP("pwm3", jz4740_pwm_pwm3),
> + INGENIC_PIN_GROUP("pwm4", jz4740_pwm_pwm4),
> + INGENIC_PIN_GROUP("pwm5", jz4740_pwm_pwm5),
> + INGENIC_PIN_GROUP("pwm6", jz4740_pwm_pwm6),
> + INGENIC_PIN_GROUP("pwm7", jz4740_pwm_pwm7),
> +};
> +
> +static const char *jz4740_mmc_groups[] = { "mmc-1bit", "mmc-4bit", };
> +static const char *jz4740_uart0_groups[] = { "uart0-data",
> "uart0-hwflow", };
> +static const char *jz4740_uart1_groups[] = { "uart1-data", };
> +static const char *jz4740_lcd_groups[] = {
> + "lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-18bit-tft", "lcd-no-pins",
> +};
> +static const char *jz4740_nand_groups[] = {
> + "nand-cs1", "nand-cs2", "nand-cs3", "nand-cs4",
> +};
> +static const char *jz4740_pwm0_groups[] = { "pwm0", };
> +static const char *jz4740_pwm1_groups[] = { "pwm1", };
> +static const char *jz4740_pwm2_groups[] = { "pwm2", };
> +static const char *jz4740_pwm3_groups[] = { "pwm3", };
> +static const char *jz4740_pwm4_groups[] = { "pwm4", };
> +static const char *jz4740_pwm5_groups[] = { "pwm5", };
> +static const char *jz4740_pwm6_groups[] = { "pwm6", };
> +static const char *jz4740_pwm7_groups[] = { "pwm7", };
> +
> +static const struct function_desc jz4740_functions[] = {
> + { "mmc", jz4740_mmc_groups, ARRAY_SIZE(jz4740_mmc_groups), },
> + { "uart0", jz4740_uart0_groups, ARRAY_SIZE(jz4740_uart0_groups), },
> + { "uart1", jz4740_uart1_groups, ARRAY_SIZE(jz4740_uart1_groups), },
> + { "lcd", jz4740_lcd_groups, ARRAY_SIZE(jz4740_lcd_groups), },
> + { "nand", jz4740_nand_groups, ARRAY_SIZE(jz4740_nand_groups), },
> + { "pwm0", jz4740_pwm0_groups, ARRAY_SIZE(jz4740_pwm0_groups), },
> + { "pwm1", jz4740_pwm1_groups, ARRAY_SIZE(jz4740_pwm1_groups), },
> + { "pwm2", jz4740_pwm2_groups, ARRAY_SIZE(jz4740_pwm2_groups), },
> + { "pwm3", jz4740_pwm3_groups, ARRAY_SIZE(jz4740_pwm3_groups), },
> + { "pwm4", jz4740_pwm4_groups, ARRAY_SIZE(jz4740_pwm4_groups), },
> + { "pwm5", jz4740_pwm5_groups, ARRAY_SIZE(jz4740_pwm5_groups), },
> + { "pwm6", jz4740_pwm6_groups, ARRAY_SIZE(jz4740_pwm6_groups), },
> + { "pwm7", jz4740_pwm7_groups, ARRAY_SIZE(jz4740_pwm7_groups), },
> +};
> +
> +static const struct ingenic_chip_info jz4740_chip_info = {
> + .num_chips = 4,
> + .groups = jz4740_groups,
> + .num_groups = ARRAY_SIZE(jz4740_groups),
> + .functions = jz4740_functions,
> + .num_functions = ARRAY_SIZE(jz4740_functions),
> + .pull_ups = jz4740_pull_ups,
> + .pull_downs = jz4740_pull_downs,
> +};
> +
> +static const u32 jz4770_pull_ups[6] = {
> + 0x3fffffff, 0xfff0030c, 0xffffffff, 0xffff4fff, 0xfffffb7c,
> 0xffa7f00f,
> +};
> +
> +static const u32 jz4770_pull_downs[6] = {
> + 0x00000000, 0x000f0c03, 0x00000000, 0x0000b000, 0x00000483,
> 0x00580ff0,
> +};
> +
> +static int jz4770_uart0_data_pins[] = { 0xa0, 0xa3, };
> +static int jz4770_uart0_hwflow_pins[] = { 0xa1, 0xa2, };
> +static int jz4770_uart1_data_pins[] = { 0x7a, 0x7c, };
> +static int jz4770_uart1_hwflow_pins[] = { 0x7b, 0x7d, };
> +static int jz4770_uart2_data_pins[] = { 0x66, 0x67, };
> +static int jz4770_uart2_hwflow_pins[] = { 0x65, 0x64, };
> +static int jz4770_uart3_data_pins[] = { 0x6c, 0x85, };
> +static int jz4770_uart3_hwflow_pins[] = { 0x88, 0x89, };
> +static int jz4770_uart4_data_pins[] = { 0x54, 0x4a, };
> +static int jz4770_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18,
> };
> +static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, };
> +static int jz4770_mmc0_1bit_a_pins[] = { 0x12, 0x13, 0x14, };
> +static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
> +static int jz4770_mmc0_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
> +static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, };
> +static int jz4770_mmc1_1bit_d_pins[] = { 0x78, 0x79, 0x74, };
> +static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
> +static int jz4770_mmc1_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
> +static int jz4770_nemc_data_pins[] = {
> + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
> +};
> +static int jz4770_nemc_cle_ale_pins[] = { 0x20, 0x21, };
> +static int jz4770_nemc_addr_pins[] = { 0x22, 0x23, 0x24, 0x25, };
> +static int jz4770_nemc_rd_we_pins[] = { 0x10, 0x11, };
> +static int jz4770_nemc_frd_fwe_pins[] = { 0x12, 0x13, };
> +static int jz4770_nemc_cs1_pins[] = { 0x15, };
> +static int jz4770_nemc_cs2_pins[] = { 0x16, };
> +static int jz4770_nemc_cs3_pins[] = { 0x17, };
> +static int jz4770_nemc_cs4_pins[] = { 0x18, };
> +static int jz4770_nemc_cs5_pins[] = { 0x19, };
> +static int jz4770_nemc_cs6_pins[] = { 0x1a, };
> +static int jz4770_i2c0_pins[] = { 0x6e, 0x6f, };
> +static int jz4770_i2c1_pins[] = { 0x8e, 0x8f, };
> +static int jz4770_i2c2_pins[] = { 0xb0, 0xb1, };
> +static int jz4770_i2c3_pins[] = { 0x6a, 0x6b, };
> +static int jz4770_i2c4_e_pins[] = { 0x8c, 0x8d, };
> +static int jz4770_i2c4_f_pins[] = { 0xb9, 0xb8, };
> +static int jz4770_cim_pins[] = {
> + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
> 0x31,
> +};
> +static int jz4770_lcd_32bit_pins[] = {
> + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
> + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
> + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
> + 0x58, 0x59, 0x51,
> +};
> +static int jz4770_pwm_pwm0_pins[] = { 0x80, };
> +static int jz4770_pwm_pwm1_pins[] = { 0x81, };
> +static int jz4770_pwm_pwm2_pins[] = { 0x82, };
> +static int jz4770_pwm_pwm3_pins[] = { 0x83, };
> +static int jz4770_pwm_pwm4_pins[] = { 0x84, };
> +static int jz4770_pwm_pwm5_pins[] = { 0x85, };
> +static int jz4770_pwm_pwm6_pins[] = { 0x6a, };
> +static int jz4770_pwm_pwm7_pins[] = { 0x6b, };
> +
> +static int jz4770_uart0_data_funcs[] = { 0, 0, };
> +static int jz4770_uart0_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart1_data_funcs[] = { 0, 0, };
> +static int jz4770_uart1_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart2_data_funcs[] = { 1, 1, };
> +static int jz4770_uart2_hwflow_funcs[] = { 1, 1, };
> +static int jz4770_uart3_data_funcs[] = { 0, 1, };
> +static int jz4770_uart3_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart4_data_funcs[] = { 2, 2, };
> +static int jz4770_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, };
> +static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, };
> +static int jz4770_mmc0_1bit_a_funcs[] = { 1, 1, 0, };
> +static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc0_1bit_e_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_1bit_d_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, };
> +static int jz4770_mmc1_1bit_e_funcs[] = { 1, 1, 1, };
> +static int jz4770_nemc_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
> +static int jz4770_nemc_cle_ale_funcs[] = { 0, 0, };
> +static int jz4770_nemc_addr_funcs[] = { 0, 0, 0, 0, };
> +static int jz4770_nemc_rd_we_funcs[] = { 0, 0, };
> +static int jz4770_nemc_frd_fwe_funcs[] = { 0, 0, };
> +static int jz4770_nemc_cs1_funcs[] = { 0, };
> +static int jz4770_nemc_cs2_funcs[] = { 0, };
> +static int jz4770_nemc_cs3_funcs[] = { 0, };
> +static int jz4770_nemc_cs4_funcs[] = { 0, };
> +static int jz4770_nemc_cs5_funcs[] = { 0, };
> +static int jz4770_nemc_cs6_funcs[] = { 0, };
> +static int jz4770_i2c0_funcs[] = { 0, 0, };
> +static int jz4770_i2c1_funcs[] = { 0, 0, };
> +static int jz4770_i2c2_funcs[] = { 2, 2, };
> +static int jz4770_i2c3_funcs[] = { 1, 1, };
> +static int jz4770_i2c4_e_funcs[] = { 1, 1, };
> +static int jz4770_i2c4_f_funcs[] = { 1, 1, };
> +static int jz4770_cim_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> };
> +static int jz4770_lcd_32bit_funcs[] = {
> + 0, 0, 0, 0, 0, 0, 0, 0,
> + 0, 0, 0, 0, 0, 0, 0, 0,
> + 0, 0, 0,
> +};
> +static int jz4770_pwm_pwm0_funcs[] = { 0, };
> +static int jz4770_pwm_pwm1_funcs[] = { 0, };
> +static int jz4770_pwm_pwm2_funcs[] = { 0, };
> +static int jz4770_pwm_pwm3_funcs[] = { 0, };
> +static int jz4770_pwm_pwm4_funcs[] = { 0, };
> +static int jz4770_pwm_pwm5_funcs[] = { 0, };
> +static int jz4770_pwm_pwm6_funcs[] = { 0, };
> +static int jz4770_pwm_pwm7_funcs[] = { 0, };
> +
> +static const struct group_desc jz4770_groups[] = {
> + INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data),
> + INGENIC_PIN_GROUP("uart0-hwflow", jz4770_uart0_hwflow),
> + INGENIC_PIN_GROUP("uart1-data", jz4770_uart1_data),
> + INGENIC_PIN_GROUP("uart1-hwflow", jz4770_uart1_hwflow),
> + INGENIC_PIN_GROUP("uart2-data", jz4770_uart2_data),
> + INGENIC_PIN_GROUP("uart2-hwflow", jz4770_uart2_hwflow),
> + INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data),
> + INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow),
> + INGENIC_PIN_GROUP("uart4-data", jz4770_uart4_data),
> + INGENIC_PIN_GROUP("mmc0-8bit-a", jz4770_mmc0_8bit_a),
> + INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a),
> + INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a),
> + INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e),
> + INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e),
> + INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d),
> + INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d),
> + INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e),
> + INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e),
> + INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_data),
> + INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale),
> + INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr),
> + INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we),
> + INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe),
> + INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1),
> + INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2),
> + INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3),
> + INGENIC_PIN_GROUP("nemc-cs4", jz4770_nemc_cs4),
> + INGENIC_PIN_GROUP("nemc-cs5", jz4770_nemc_cs5),
> + INGENIC_PIN_GROUP("nemc-cs6", jz4770_nemc_cs6),
> + INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0),
> + INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1),
> + INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2),
> + INGENIC_PIN_GROUP("i2c3-data", jz4770_i2c3),
> + INGENIC_PIN_GROUP("i2c4-data-e", jz4770_i2c4_e),
> + INGENIC_PIN_GROUP("i2c4-data-f", jz4770_i2c4_f),
> + INGENIC_PIN_GROUP("cim-data", jz4770_cim),
> + INGENIC_PIN_GROUP("lcd-32bit", jz4770_lcd_32bit),
> + { "lcd-no-pins", },
> + INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0),
> + INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1),
> + INGENIC_PIN_GROUP("pwm2", jz4770_pwm_pwm2),
> + INGENIC_PIN_GROUP("pwm3", jz4770_pwm_pwm3),
> + INGENIC_PIN_GROUP("pwm4", jz4770_pwm_pwm4),
> + INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5),
> + INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6),
> + INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7),
> +};
> +
> +static const char *jz4770_uart0_groups[] = { "uart0-data",
> "uart0-hwflow", };
> +static const char *jz4770_uart1_groups[] = { "uart1-data",
> "uart1-hwflow", };
> +static const char *jz4770_uart2_groups[] = { "uart2-data",
> "uart2-hwflow", };
> +static const char *jz4770_uart3_groups[] = { "uart3-data",
> "uart3-hwflow", };
> +static const char *jz4770_uart4_groups[] = { "uart4-data", };
> +static const char *jz4770_mmc0_groups[] = {
> + "mmc0-8bit-a", "mmc0-4bit-a", "mmc0-1bit-a",
> + "mmc0-1bit-e", "mmc0-4bit-e",
> +};
> +static const char *jz4770_mmc1_groups[] = {
> + "mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e",
> +};
> +static const char *jz4770_nemc_groups[] = {
> + "nemc-data", "nemc-cle-ale", "nemc-addr", "nemc-rd-we",
> "nemc-frd-fwe",
> +};
> +static const char *jz4770_cs1_groups[] = { "nemc-cs1", };
> +static const char *jz4770_cs6_groups[] = { "nemc-cs6", };
> +static const char *jz4770_i2c0_groups[] = { "i2c0-data", };
> +static const char *jz4770_i2c1_groups[] = { "i2c1-data", };
> +static const char *jz4770_i2c2_groups[] = { "i2c2-data", };
> +static const char *jz4770_i2c3_groups[] = { "i2c3-data", };
> +static const char *jz4770_i2c4_groups[] = { "i2c4-data-e",
> "i2c4-data-f", };
> +static const char *jz4770_cim_groups[] = { "cim-data", };
> +static const char *jz4770_lcd_groups[] = { "lcd-32bit", "lcd-no-pins",
> };
> +static const char *jz4770_pwm0_groups[] = { "pwm0", };
> +static const char *jz4770_pwm1_groups[] = { "pwm1", };
> +static const char *jz4770_pwm2_groups[] = { "pwm2", };
> +static const char *jz4770_pwm3_groups[] = { "pwm3", };
> +static const char *jz4770_pwm4_groups[] = { "pwm4", };
> +static const char *jz4770_pwm5_groups[] = { "pwm5", };
> +static const char *jz4770_pwm6_groups[] = { "pwm6", };
> +static const char *jz4770_pwm7_groups[] = { "pwm7", };
> +
> +static const struct function_desc jz4770_functions[] = {
> + { "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), },
> + { "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), },
> + { "uart2", jz4770_uart2_groups, ARRAY_SIZE(jz4770_uart2_groups), },
> + { "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), },
> + { "uart4", jz4770_uart4_groups, ARRAY_SIZE(jz4770_uart4_groups), },
> + { "mmc0", jz4770_mmc0_groups, ARRAY_SIZE(jz4770_mmc0_groups), },
> + { "mmc1", jz4770_mmc1_groups, ARRAY_SIZE(jz4770_mmc1_groups), },
> + { "nemc", jz4770_nemc_groups, ARRAY_SIZE(jz4770_nemc_groups), },
> + { "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), },
> + { "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), },
> + { "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), },
> + { "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), },
> + { "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), },
> + { "i2c3", jz4770_i2c3_groups, ARRAY_SIZE(jz4770_i2c3_groups), },
> + { "i2c4", jz4770_i2c4_groups, ARRAY_SIZE(jz4770_i2c4_groups), },
> + { "cim", jz4770_cim_groups, ARRAY_SIZE(jz4770_cim_groups), },
> + { "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), },
> + { "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), },
> + { "pwm1", jz4770_pwm1_groups, ARRAY_SIZE(jz4770_pwm1_groups), },
> + { "pwm2", jz4770_pwm2_groups, ARRAY_SIZE(jz4770_pwm2_groups), },
> + { "pwm3", jz4770_pwm3_groups, ARRAY_SIZE(jz4770_pwm3_groups), },
> + { "pwm4", jz4770_pwm4_groups, ARRAY_SIZE(jz4770_pwm4_groups), },
> + { "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), },
> + { "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), },
> + { "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), },
> +};
> +
> +static const struct ingenic_chip_info jz4770_chip_info = {
> + .num_chips = 6,
> + .groups = jz4770_groups,
> + .num_groups = ARRAY_SIZE(jz4770_groups),
> + .functions = jz4770_functions,
> + .num_functions = ARRAY_SIZE(jz4770_functions),
> + .pull_ups = jz4770_pull_ups,
> + .pull_downs = jz4770_pull_downs,
> +};
> +
> +static inline void ingenic_config_pin(struct ingenic_pinctrl *jzpc,
> + unsigned int pin, u8 reg, bool set)
> +{
> + unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> + unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> + regmap_write(jzpc->map, offt * 0x100 +
> + (set ? REG_SET(reg) : REG_CLEAR(reg)), BIT(idx));
> +}
> +
> +static inline bool ingenic_get_pin_config(struct ingenic_pinctrl
> *jzpc,
> + unsigned int pin, u8 reg)
> +{
> + unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> + unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> + unsigned int val;
> +
> + regmap_read(jzpc->map, offt * 0x100 + reg, &val);
> +
> + return val & BIT(idx);
> +}
> +
> +static struct pinctrl_ops ingenic_pctlops = {
> + .get_groups_count = pinctrl_generic_get_group_count,
> + .get_group_name = pinctrl_generic_get_group_name,
> + .get_group_pins = pinctrl_generic_get_group_pins,
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
> + .dt_free_map = pinconf_generic_dt_free_map,
> +};
> +
> +static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc,
> + int pin, int func)
> +{
> + unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> + unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> + dev_dbg(jzpc->dev, "set pin P%c%u to function %u\n",
> + 'A' + offt, idx, func);
> +
> + if (jzpc->version >= ID_JZ4770) {
> + ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
> + ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, false);
> + ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, func & 0x2);
> + ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT0, func & 0x1);
> + } else {
> + ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, true);
> + ingenic_config_pin(jzpc, pin, JZ4740_GPIO_TRIG, func & 0x2);
> + ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, func > 0);
> + }
> +
> + return 0;
> +}
> +
> +static int ingenic_pinmux_set_mux(struct pinctrl_dev *pctldev,
> + unsigned int selector, unsigned int group)
> +{
> + struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> + struct function_desc *func;
> + struct group_desc *grp;
> + unsigned int i;
> +
> + func = pinmux_generic_get_function(pctldev, selector);
> + if (!func)
> + return -EINVAL;
> +
> + grp = pinctrl_generic_get_group(pctldev, group);
> + if (!grp)
> + return -EINVAL;
> +
> + dev_dbg(pctldev->dev, "enable function %s group %s\n",
> + func->name, grp->name);
> +
> + for (i = 0; i < grp->num_pins; i++) {
> + int *pin_modes = grp->data;
> +
> + ingenic_pinmux_set_pin_fn(jzpc, grp->pins[i], pin_modes[i]);
> + }
> +
> + return 0;
> +}
> +
> +static int ingenic_pinmux_gpio_set_direction(struct pinctrl_dev
> *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned int pin, bool input)
> +{
> + struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> + unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> + unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> + dev_dbg(pctldev->dev, "set pin P%c%u to %sput\n",
> + 'A' + offt, idx, input ? "in" : "out");
> +
> + if (jzpc->version >= ID_JZ4770) {
> + ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
> + ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, true);
> + ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, input);
> + } else {
> + ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, false);
> + ingenic_config_pin(jzpc, pin, JZ4740_GPIO_DIR, input);
> + ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, false);
> + }
> +
> + return 0;
> +}
> +
> +static struct pinmux_ops ingenic_pmxops = {
> + .get_functions_count = pinmux_generic_get_function_count,
> + .get_function_name = pinmux_generic_get_function_name,
> + .get_function_groups = pinmux_generic_get_function_groups,
> + .set_mux = ingenic_pinmux_set_mux,
> + .gpio_set_direction = ingenic_pinmux_gpio_set_direction,
> +};
> +
> +static int ingenic_pinconf_get(struct pinctrl_dev *pctldev,
> + unsigned int pin, unsigned long *config)
> +{
> + struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> + enum pin_config_param param = pinconf_to_config_param(*config);
> + unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> + unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> + bool pull;
> +
> + if (jzpc->version >= ID_JZ4770)
> + pull = !ingenic_get_pin_config(jzpc, pin, JZ4770_GPIO_PEN);
> + else
> + pull = !ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_PULL_DIS);
> +
> + switch (param) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + if (pull)
> + return -EINVAL;
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_UP:
> + if (!pull || !(jzpc->info->pull_ups[offt] & BIT(idx)))
> + return -EINVAL;
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + if (!pull || !(jzpc->info->pull_downs[offt] & BIT(idx)))
> + return -EINVAL;
> + break;
> +
> + default:
> + return -ENOTSUPP;
> + }
> +
> + *config = pinconf_to_config_packed(param, 1);
> + return 0;
> +}
> +
> +static void ingenic_set_bias(struct ingenic_pinctrl *jzpc,
> + unsigned int pin, bool enabled)
> +{
> + if (jzpc->version >= ID_JZ4770)
> + ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PEN, !enabled);
> + else
> + ingenic_config_pin(jzpc, pin, JZ4740_GPIO_PULL_DIS, !enabled);
> +}
> +
> +static int ingenic_pinconf_set(struct pinctrl_dev *pctldev, unsigned
> int pin,
> + unsigned long *configs, unsigned int num_configs)
> +{
> + struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> + unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> + unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> + unsigned int cfg;
> +
> + for (cfg = 0; cfg < num_configs; cfg++) {
> + switch (pinconf_to_config_param(configs[cfg])) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + case PIN_CONFIG_BIAS_PULL_UP:
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + continue;
> + default:
> + return -ENOTSUPP;
> + }
> + }
> +
> + for (cfg = 0; cfg < num_configs; cfg++) {
> + switch (pinconf_to_config_param(configs[cfg])) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + dev_dbg(jzpc->dev, "disable pull-over for pin P%c%u\n",
> + 'A' + offt, idx);
> + ingenic_set_bias(jzpc, pin, false);
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_UP:
> + if (!(jzpc->info->pull_ups[offt] & BIT(idx)))
> + return -EINVAL;
> + dev_dbg(jzpc->dev, "set pull-up for pin P%c%u\n",
> + 'A' + offt, idx);
> + ingenic_set_bias(jzpc, pin, true);
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + if (!(jzpc->info->pull_downs[offt] & BIT(idx)))
> + return -EINVAL;
> + dev_dbg(jzpc->dev, "set pull-down for pin P%c%u\n",
> + 'A' + offt, idx);
> + ingenic_set_bias(jzpc, pin, true);
> + break;
> +
> + default:
> + unreachable();
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int ingenic_pinconf_group_get(struct pinctrl_dev *pctldev,
> + unsigned int group, unsigned long *config)
> +{
> + const unsigned int *pins;
> + unsigned int i, npins, old = 0;
> + int ret;
> +
> + ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < npins; i++) {
> + if (ingenic_pinconf_get(pctldev, pins[i], config))
> + return -ENOTSUPP;
> +
> + /* configs do not match between two pins */
> + if (i && (old != *config))
> + return -ENOTSUPP;
> +
> + old = *config;
> + }
> +
> + return 0;
> +}
> +
> +static int ingenic_pinconf_group_set(struct pinctrl_dev *pctldev,
> + unsigned int group, unsigned long *configs,
> + unsigned int num_configs)
> +{
> + const unsigned int *pins;
> + unsigned int i, npins;
> + int ret;
> +
> + ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < npins; i++) {
> + ret = ingenic_pinconf_set(pctldev,
> + pins[i], configs, num_configs);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static struct pinconf_ops ingenic_confops = {
> + .is_generic = true,
> + .pin_config_get = ingenic_pinconf_get,
> + .pin_config_set = ingenic_pinconf_set,
> + .pin_config_group_get = ingenic_pinconf_group_get,
> + .pin_config_group_set = ingenic_pinconf_group_set,
> +};
> +
> +static const struct regmap_config ingenic_pinctrl_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> +};
> +
> +static const struct of_device_id ingenic_pinctrl_of_match[] = {
> + { .compatible = "ingenic,jz4740-pinctrl", .data = (void *) ID_JZ4740
> },
> + { .compatible = "ingenic,jz4770-pinctrl", .data = (void *) ID_JZ4770
> },
> + { .compatible = "ingenic,jz4780-pinctrl", .data = (void *) ID_JZ4780
> },
> + {},
> +};
> +
> +int ingenic_pinctrl_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct ingenic_pinctrl *jzpc;
> + struct pinctrl_desc *pctl_desc;
> + void __iomem *base;
> + const struct platform_device_id *id = platform_get_device_id(pdev);
> + const struct of_device_id *of_id = of_match_device(
> + ingenic_pinctrl_of_match, dev);
> + const struct ingenic_chip_info *chip_info;
> + unsigned int i;
> + int err;
> +
> + jzpc = devm_kzalloc(dev, sizeof(*jzpc), GFP_KERNEL);
> + if (!jzpc)
> + return -ENOMEM;
> +
> + base = devm_ioremap_resource(dev,
> + platform_get_resource(pdev, IORESOURCE_MEM, 0));
> + if (IS_ERR(base)) {
> + dev_err(dev, "Failed to ioremap registers\n");
> + return PTR_ERR(base);
> + }
> +
> + jzpc->map = devm_regmap_init_mmio(dev, base,
> + &ingenic_pinctrl_regmap_config);
> + if (IS_ERR(jzpc->map)) {
> + dev_err(dev, "Failed to create regmap\n");
> + return PTR_ERR(jzpc->map);
> + }
> +
> + jzpc->dev = dev;
> +
> + if (of_id)
> + jzpc->version = (enum jz_version)of_id->data;
> + else
> + jzpc->version = (enum jz_version)id->driver_data;
> +
> + if (jzpc->version >= ID_JZ4770)
> + chip_info = &jz4770_chip_info;
> + else
> + chip_info = &jz4740_chip_info;
> + jzpc->info = chip_info;
> +
> + pctl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctl_desc), GFP_KERNEL);
> + if (!pctl_desc)
> + return -ENOMEM;
> +
> + /* fill in pinctrl_desc structure */
> + pctl_desc->name = dev_name(dev);
> + pctl_desc->owner = THIS_MODULE;
> + pctl_desc->pctlops = &ingenic_pctlops;
> + pctl_desc->pmxops = &ingenic_pmxops;
> + pctl_desc->confops = &ingenic_confops;
> + pctl_desc->npins = chip_info->num_chips * PINS_PER_GPIO_CHIP;
> + pctl_desc->pins = jzpc->pdesc = devm_kzalloc(&pdev->dev,
> + sizeof(*jzpc->pdesc) * pctl_desc->npins, GFP_KERNEL);
> + if (!jzpc->pdesc)
> + return -ENOMEM;
> +
> + for (i = 0; i < pctl_desc->npins; i++) {
> + jzpc->pdesc[i].number = i;
> + jzpc->pdesc[i].name = kasprintf(GFP_KERNEL, "P%c%d",
> + 'A' + (i / PINS_PER_GPIO_CHIP),
> + i % PINS_PER_GPIO_CHIP);
> + }
> +
> + jzpc->pctl = devm_pinctrl_register(dev, pctl_desc, jzpc);
> + if (!jzpc->pctl) {
> + dev_err(dev, "Failed to register pinctrl\n");
> + return -EINVAL;
> + }
> +
> + for (i = 0; i < chip_info->num_groups; i++) {
> + const struct group_desc *group = &chip_info->groups[i];
> +
> + err = pinctrl_generic_add_group(jzpc->pctl, group->name,
> + group->pins, group->num_pins, group->data);
> + if (err) {
> + dev_err(dev, "Failed to register group %s\n",
> + group->name);
> + return err;
> + }
> + }
> +
> + for (i = 0; i < chip_info->num_functions; i++) {
> + const struct function_desc *func = &chip_info->functions[i];
> +
> + err = pinmux_generic_add_function(jzpc->pctl, func->name,
> + func->group_names, func->num_group_names,
> + func->data);
> + if (err) {
> + dev_err(dev, "Failed to register function %s\n",
> + func->name);
> + return err;
> + }
> + }
> +
> + dev_set_drvdata(dev, jzpc->map);
> +
> + if (dev->of_node) {
> + err = of_platform_populate(dev->of_node, NULL, NULL, dev);
> + if (err) {
> + dev_err(dev, "Failed to probe GPIO devices\n");
> + return err;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct platform_device_id ingenic_pinctrl_ids[] = {
> + { "jz4740-pinctrl", ID_JZ4740 },
> + { "jz4770-pinctrl", ID_JZ4770 },
> + { "jz4780-pinctrl", ID_JZ4780 },
> + {},
> +};
> +
> +static struct platform_driver ingenic_pinctrl_driver = {
> + .driver = {
> + .name = "pinctrl-ingenic",
> + .of_match_table = of_match_ptr(ingenic_pinctrl_of_match),
> + .suppress_bind_attrs = true,
> + },
> + .probe = ingenic_pinctrl_probe,
> + .id_table = ingenic_pinctrl_ids,
> +};
> +
> +static int __init ingenic_pinctrl_drv_register(void)
> +{
> + return platform_driver_register(&ingenic_pinctrl_driver);
> +}
> +postcore_initcall(ingenic_pinctrl_drv_register);
^ permalink raw reply
* Re: [PATCH v5] media: platform: Renesas IMR driver
From: Laurent Pinchart @ 2017-05-03 9:04 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Mauro Carvalho Chehab,
devicetree@vger.kernel.org, Linux Media Mailing List,
Linux-Renesas, Konstantin Kozhevnikov, Kuninori Morimoto
In-Reply-To: <CAMuHMdWE+o3gsFnxqBcvrD=PfHHb0i9uK3tsfWaNxfuhK3SNKg@mail.gmail.com>
Hi Geert,
On Wednesday 03 May 2017 09:22:00 Geert Uytterhoeven wrote:
> On Tue, May 2, 2017 at 11:17 PM, Laurent Pinchart wrote:
> > On Wednesday 22 Mar 2017 10:34:16 Geert Uytterhoeven wrote:
> >> On Thu, Mar 9, 2017 at 9:08 PM, Sergei Shtylyov wrote:
> >>> --- /dev/null
> >>> +++ media_tree/Documentation/devicetree/bindings/media/rcar_imr.txt
> >>> @@ -0,0 +1,27 @@
> >>> +Renesas R-Car Image Renderer (Distortion Correction Engine)
> >>> +-----------------------------------------------------------
> >>> +
> >>> +The image renderer, or the distortion correction engine, is a drawing
> >>> processor
> >>> +with a simple instruction system capable of referencing video capture
> >>> data or
> >>> +data in an external memory as 2D texture data and performing texture
> >>> mapping
> >>> +and drawing with respect to any shape that is split into triangular
> >>> objects.
> >>> +
> >>> +Required properties:
> >>> +
> >>> +- compatible: "renesas,<soctype>-imr-lx4", "renesas,imr-lx4" as a
> >>> fallback for
> >>> + the image renderer light extended 4 (IMR-LX4) found in the R-Car
> >>> gen3 SoCs,
> >>> + where the examples with <soctype> are:
> >>> + - "renesas,r8a7795-imr-lx4" for R-Car H3,
> >>> + - "renesas,r8a7796-imr-lx4" for R-Car M3-W.
> >>
> >> Laurent: what do you think about the need for SoC-specific compatible
> >> values for the various IM* blocks?
> >
> > There's no documented IP core version register, but when dumping all
> > configuration registers on H3 and M3-W I noticed that register 0x002c, not
> > documented in the datasheet, reads 0x14060514 on all four IMR instances in
> > H3, and 0x20150505 on both instances in M3-W.
> >
> > This looks like a version register to me. If my assumption is correct, we
> > could do without any SoC-specific compatible string.
>
> I read this assumed version registers on all R-Car SoCs, after writing
> zero to 0xe6150990 (SMSTPCR8).
>
> IMR-X2 on R-Car H2: 0x12072009
> IMR-LSX2 on R-Car H2: 0x12072009
> IMR-LSX3 on R-Car V2H: 0x13052617
> IMR-LX2 on R-Car M2-W: 0x12072009
> IMR-LX2 on R-Car M2-N: 0x12072009
> IMR-LX2 on R-Car E2: 0x13091909
> IMR-LX3 on R-Car V2H: 0x13052617
>
> Note that several IDs are the same, but you know the type from the
> compatible value.
>
> It would be good to get confirmation from the hardware team that this is
> indeed a version register.
Thank you for checking.
Morimoto-san, do you think there are still people alive in the Gen2 hardware
team who could provide the information ? :-) If not, information restricted to
Gen3 would still be useful.
--
Regards,
Laurent Pinchart
^ permalink raw reply
* Re: [PATCH 13/15] drm/sun4i: Add HDMI support
From: Maxime Ripard @ 2017-05-03 8:41 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Mike Turquette, Stephen Boyd, dri-devel, Daniel Vetter,
David Airlie, Mark Rutland, Rob Herring, devicetree, linux-clk,
linux-arm-kernel, linux-kernel, linux-sunxi
In-Reply-To: <CAGb2v67Szt9Xj+hwJD6i9X0XgXOtZBS1Jy4wWjbfSGJSDFM1NQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 3950 bytes --]
On Wed, Apr 26, 2017 at 03:59:28PM +0800, Chen-Yu Tsai wrote:
> >> > + writel(SUN4I_HDMI_VID_TIMING_X(mode->hdisplay) |
> >> > + SUN4I_HDMI_VID_TIMING_Y(mode->vdisplay),
> >> > + hdmi->base + SUN4I_HDMI_VID_TIMING_ACT_REG);
> >> > +
> >> > + x = mode->htotal - mode->hsync_start;
> >> > + y = mode->vtotal - mode->vsync_start;
> >>
> >> I'm a bit skeptical about this one. All the other parameters are not
> >> inclusive of other, why would this one be different? Shouldn't it
> >> be "Xtotal - Xsync_end" instead?
> >
> > By the usual meaning of backporch, you're right. However, Allwinner's
> > seems to have it's own, which is actually the backporch + sync length.
> >
> > We also have that on all the other connectors (and TCON), and this was
> > confirmed at the time using a scope on an RGB signal.
>
> Yes. On the later SoCs such as the A31, the user manual actually has
> timing diagrams showing this.
>
> Unlike the TCON, the HDMI controller's timings lists the front porch
> separately, instead of an all inclusive Xtotal. This is what made me
> look twice. This should be easy to confirm though. Since the HDMI modes
> are well known and can be exactly reproduced on our hardware, we can
> just check for any distortions or refresh rate errors.
This isn't as trivial as that. Screens usually have some tolerancies
on the timings, which will probably make it go unnoticed, even though
they are wrong.
> >>
> >> > + writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
> >> > + hdmi->base + SUN4I_HDMI_VID_TIMING_BP_REG);
> >> > +
> >> > + x = mode->hsync_start - mode->hdisplay;
> >> > + y = mode->vsync_start - mode->vdisplay;
> >> > + writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
> >> > + hdmi->base + SUN4I_HDMI_VID_TIMING_FP_REG);
> >> > +
> >> > + x = mode->hsync_end - mode->hsync_start;
> >> > + y = mode->vsync_end - mode->vsync_start;
> >> > + writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
> >> > + hdmi->base + SUN4I_HDMI_VID_TIMING_SPW_REG);
> >> > +
> >> > + val = SUN4I_HDMI_VID_TIMING_POL_TX_CLK;
> >> > + if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> >> > + val |= SUN4I_HDMI_VID_TIMING_POL_HSYNC;
> >> > +
> >> > + if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> >> > + val |= SUN4I_HDMI_VID_TIMING_POL_VSYNC;
> >> > +
> >> > + writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG);
> >>
> >> You don't handle the interlaced video here, even though you set
> >>
> >> hdmi->connector.interlace_allowed = true
> >>
> >> later.
> >
> > I'll fix that.
> >
> >> The double clock and double scan flags aren't handled either, though
> >> I don't understand which one is supposed to represent the need for the
> >> HDMI pixel repeater. AFAIK this is required for resolutions with pixel
> >> clocks lower than 25 MHz, the lower limit of HDMI's TMDS link.
> >
> > I'm not sure about this one though. I'd like to keep things quite
> > simple for now and build up on that once the basis is working. Is it
> > common in the wild?
>
> If you drive the display at SDTV resolutions, then yes. Mode lines from
> my HDMI monitor:
>
> 720x576i 50 720 732 795 864 576 580 586 625 flags: nhsync, nvsync,
> interlace, dblclk; type: driver
> 720x480i 60 720 739 801 858 480 488 494 525 flags: nhsync, nvsync,
> interlace, dblclk; type: driver
> 720x480i 60 720 739 801 858 480 488 494 525 flags: nhsync, nvsync,
> interlace, dblclk; type: driver
>
> AFAIK these are standard modes that all devices should support. Whether
> they are used daily is another thing. Maybe block modes with dblclk
> in .mode_fixup for now?
That would rather be atomic_check and / or mode_valid, but yeah, I can
do that.
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: [PATCH v6 5/8] i2c: mux: pca954x: Call request irq after adding mux segments
From: Phil Reid @ 2017-05-03 8:36 UTC (permalink / raw)
To: Peter Rosin, wsa, robh+dt, mark.rutland, sre, linux-i2c,
devicetree, linux-pm, benjamin.tissoires
In-Reply-To: <28f3ce1d-ad4b-3082-7499-b30245a256c1@axentia.se>
On 2/05/2017 17:57, Peter Rosin wrote:
> On 2017-05-02 11:12, Phil Reid wrote:
>> The pca954x device do not have the ability to mask interrupts. For
>> i2c slave devices that also don't have masking ability (eg ltc1760
>> smbalert output) delay registering the irq until after the mux
>> segments have been configured. During the mux add_adaptor call the
>> core i2c system can register an smbalert handler which would then
>> be called immediately when the irq is registered. This smbalert
>> handler will then clear the pending irq.
>>
>> Signed-off-by: Phil Reid <preid@electromag.com.au>
>> ---
>> drivers/i2c/muxes/i2c-mux-pca954x.c | 52 +++++++++++++++++--------------------
>> 1 file changed, 24 insertions(+), 28 deletions(-)
>>
>> diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
>> index ad31d21..4299738 100644
>> --- a/drivers/i2c/muxes/i2c-mux-pca954x.c
>> +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
>> @@ -294,7 +294,7 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
>> {
>> struct pca954x *data = i2c_mux_priv(muxc);
>> struct i2c_client *client = data->client;
>> - int c, err, irq;
>> + int c, irq;
>>
>> if (!data->chip->has_irq || client->irq <= 0)
>> return 0;
>> @@ -314,24 +314,22 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
>> handle_simple_irq);
>> }
>>
>> - err = devm_request_threaded_irq(&client->dev, data->client->irq, NULL,
>> - pca954x_irq_handler,
>> - IRQF_ONESHOT | IRQF_SHARED,
>> - "pca954x", data);
>> - if (err)
>> - goto err_req_irq;
>> + return 0;
>> +}
>>
>> - disable_irq(data->client->irq);
>> +static void pca954x_cleanup(struct i2c_mux_core *muxc)
>> +{
>> + struct pca954x *data = i2c_mux_priv(muxc);
>> + int c, irq;
>>
>> - return 0;
>> -err_req_irq:
>> - for (c = 0; c < data->chip->nchans; c++) {
>> - irq = irq_find_mapping(data->irq, c);
>> - irq_dispose_mapping(irq);
>> + if (data->irq) {
>> + for (c = 0; c < data->chip->nchans; c++) {
>> + irq = irq_find_mapping(data->irq, c);
>> + irq_dispose_mapping(irq);
>> + }
>> + irq_domain_remove(data->irq);
>> }
>> - irq_domain_remove(data->irq);
>> -
>> - return err;
>> + i2c_mux_del_adapters(muxc);
>> }
>>
>> /*
>> @@ -422,6 +420,14 @@ static int pca954x_probe(struct i2c_client *client,
>> }
>> }
>>
>> + if (data->chip->has_irq || client->irq > 0) {
>
> This should be && instead of ||. However, I think it's better to not
> try to replicate the inverse of the test in pca954x_irq_setup and
> instead just check if the irq domain is there with "if (data->irq)".
> Assuming that is the intent...
Yeah, that's better.
>
>> + ret = devm_request_threaded_irq(&client->dev, data->client->irq,
>> + NULL, pca954x_irq_handler, IRQF_ONESHOT | IRQF_SHARED,
>> + "pca954x", data);
>
> The indentation is horrific. Please fix.
oops
>
> Acked with those two fixed.
>
> I also noticed that there is no check for failure of the call to
> irq_create_mapping in pca954x_irq_setup. For bonus points, can you
> fix that error path too, please? Or should failure to create those
> irq mappings not be fatal for some reason?
Seems reasonable to fail. I'll fix that as well.
>
> Cheers,
> peda
>
>> + if (ret)
>> + goto fail_del_adapters;
>> + }
>> +
>> dev_info(&client->dev,
>> "registered %d multiplexed busses for I2C %s %s\n",
>> num, data->chip->muxtype == pca954x_ismux
>> @@ -430,25 +436,15 @@ static int pca954x_probe(struct i2c_client *client,
>> return 0;
>>
>> fail_del_adapters:
>> - i2c_mux_del_adapters(muxc);
>> + pca954x_cleanup(muxc);
>> return ret;
>> }
>>
>> static int pca954x_remove(struct i2c_client *client)
>> {
>> struct i2c_mux_core *muxc = i2c_get_clientdata(client);
>> - struct pca954x *data = i2c_mux_priv(muxc);
>> - int c, irq;
>>
>> - if (data->irq) {
>> - for (c = 0; c < data->chip->nchans; c++) {
>> - irq = irq_find_mapping(data->irq, c);
>> - irq_dispose_mapping(irq);
>> - }
>> - irq_domain_remove(data->irq);
>> - }
>> -
>> - i2c_mux_del_adapters(muxc);
>> + pca954x_cleanup(muxc);
>> return 0;
>> }
>>
>>
>
>
>
--
Regards
Phil Reid
ElectroMagnetic Imaging Technology Pty Ltd
Development of Geophysical Instrumentation & Software
www.electromag.com.au
3 The Avenue, Midland WA 6056, AUSTRALIA
Ph: +61 8 9250 8100
Fax: +61 8 9250 7100
Email: preid@electromag.com.au
^ permalink raw reply
* Re: [PATCH 2/2] [media] platform: add video-multiplexer subdevice driver
From: Philipp Zabel @ 2017-05-03 8:35 UTC (permalink / raw)
To: Peter Rosin
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA, Steve Longerbeam,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Sascha Hauer, Rob Herring,
Sakari Ailus, Pavel Machek, Steve Longerbeam, Vladimir Zapolskiy
In-Reply-To: <74bfa70b-3407-9484-9717-bb2d07356f70-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On Tue, 2017-05-02 at 19:42 +0200, Peter Rosin wrote:
> On 2017-05-02 17:21, Philipp Zabel wrote:
> > On Sat, 2017-04-29 at 23:42 +0200, Peter Rosin wrote:
> >> On 2017-04-29 23:29, Peter Rosin wrote:
> >>> On 2017-04-28 16:13, Philipp Zabel wrote:
> >>>> This driver can handle SoC internal and external video bus multiplexers,
> >>>> controlled by mux controllers provided by the mux controller framework,
> >>>> such as MMIO register bitfields or GPIOs. The subdevice passes through
> >>>> the mbus configuration of the active input to the output side.
> >>>>
> >>>> Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >>>> Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >>>> Signed-off-by: Steve Longerbeam <steve_longerbeam-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
> >>>> ---
> >>>> This has been last sent as part of the i.MX media series.
> >>>>
> >>>> Changes since https://patchwork.kernel.org/patch/9647869/:
> >>>> - Split out the actual mux operation to be provided by the mux controller
> >>>> framework [1]. GPIO and MMIO control can be provided by individual mux
> >>>> controller drivers [2][3].
> >>>> [1] https://patchwork.kernel.org/patch/9695837/
> >>>> [2] https://patchwork.kernel.org/patch/9695839/
> >>>> [3] https://patchwork.kernel.org/patch/9704509/
> >>>> - Shortened 'video-multiplexer' to 'video-mux', replaced all instances of
> >>>> vidsw with video_mux.
> >>>> - Made the mux inactive by default, only activated by user interaction.
> >>>> - Added CONFIG_OF and CONFIG_MULTIPLEXER dependencies.
> >>>> - Reuse subdev.entity.num_pads instead of keeping our own count.
> >>>> - Removed implicit link disabling. Instead, trying to enable a second
> >>>> sink pad link yields -EBUSY.
> >>>> - Merged _async_init into _probe.
> >>>> - Removed superfluous pad index check from _set_format.
> >>>> - Added is_source_pad helper to tell source and sink pads apart.
> >>>> - Removed test for status property in endpoint nodes. Disable the remote
> >>>> device or sever the endpoint link to disable a sink pad.
> >>>> ---
> >>>> drivers/media/platform/Kconfig | 6 +
> >>>> drivers/media/platform/Makefile | 2 +
> >>>> drivers/media/platform/video-mux.c | 341 +++++++++++++++++++++++++++++++++++++
> >>>> 3 files changed, 349 insertions(+)
> >>>> create mode 100644 drivers/media/platform/video-mux.c
> >>>>
> >>>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> >>>> index c9106e105baba..b046a6d39fee5 100644
> >>>> --- a/drivers/media/platform/Kconfig
> >>>> +++ b/drivers/media/platform/Kconfig
> >>>> @@ -74,6 +74,12 @@ config VIDEO_M32R_AR_M64278
> >>>> To compile this driver as a module, choose M here: the
> >>>> module will be called arv.
> >>>>
> >>>> +config VIDEO_MUX
> >>>> + tristate "Video Multiplexer"
> >>>> + depends on OF && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER && MULTIPLEXER
> >>>> + help
> >>>> + This driver provides support for N:1 video bus multiplexers.
> >>>> +
> >>>> config VIDEO_OMAP3
> >>>> tristate "OMAP 3 Camera support"
> >>>> depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
> >>>> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> >>>> index 349ddf6a69da2..fd2735ca3ff75 100644
> >>>> --- a/drivers/media/platform/Makefile
> >>>> +++ b/drivers/media/platform/Makefile
> >>>> @@ -27,6 +27,8 @@ obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
> >>>>
> >>>> obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
> >>>>
> >>>> +obj-$(CONFIG_VIDEO_MUX) += video-mux.o
> >>>> +
> >>>> obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
> >>>> obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/
> >>>> obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
> >>>> diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
> >>>> new file mode 100644
> >>>> index 0000000000000..419541729f67e
> >>>> --- /dev/null
> >>>> +++ b/drivers/media/platform/video-mux.c
> >>>> @@ -0,0 +1,341 @@
> >>>> +/*
> >>>> + * video stream multiplexer controlled via mux control
> >>>> + *
> >>>> + * Copyright (C) 2013 Pengutronix, Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >>>> + * Copyright (C) 2016 Pengutronix, Philipp Zabel <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> >>>
> >>> 2017?
> >>>
> >>>> + *
> >>>> + * This program is free software; you can redistribute it and/or
> >>>> + * modify it under the terms of the GNU General Public License
> >>>> + * as published by the Free Software Foundation; either version 2
> >>>> + * of the License, or (at your option) any later version.
> >>>> + * This program is distributed in the hope that it will be useful,
> >>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> >>>> + * GNU General Public License for more details.
> >>>> + */
> >>>> +
> >>>> +#include <linux/err.h>
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/mux/consumer.h>
> >>>> +#include <linux/of.h>
> >>>> +#include <linux/of_graph.h>
> >>>> +#include <linux/platform_device.h>
> >>>> +#include <media/v4l2-async.h>
> >>>> +#include <media/v4l2-device.h>
> >>>> +#include <media/v4l2-subdev.h>
> >>>> +#include <media/v4l2-of.h>
> >>>> +
> >>>> +struct video_mux {
> >>>> + struct v4l2_subdev subdev;
> >>>> + struct media_pad *pads;
> >>>> + struct v4l2_mbus_framefmt *format_mbus;
> >>>> + struct v4l2_of_endpoint *endpoint;
> >>>> + struct mux_control *mux;
> >>>> + int active;
> >>>> +};
> >>>> +
> >>>> +static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev *sd)
> >>>> +{
> >>>> + return container_of(sd, struct video_mux, subdev);
> >>>> +}
> >>>> +
> >>>> +static inline bool is_source_pad(struct video_mux *vmux, unsigned int pad)
> >>>> +{
> >>>> + return pad == vmux->subdev.entity.num_pads - 1;
> >>>> +}
> >>>> +
> >>>> +static int video_mux_link_setup(struct media_entity *entity,
> >>>> + const struct media_pad *local,
> >>>> + const struct media_pad *remote, u32 flags)
> >>>> +{
> >>>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> >>>> + struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
> >>>> + int ret;
> >>>> +
> >>>> + /*
> >>>> + * The mux state is determined by the enabled sink pad link.
> >>>> + * Enabling or disabling the source pad link has no effect.
> >>>> + */
> >>>> + if (is_source_pad(vmux, local->index))
> >>>> + return 0;
> >>>> +
> >>>> + dev_dbg(sd->dev, "link setup '%s':%d->'%s':%d[%d]",
> >>>> + remote->entity->name, remote->index, local->entity->name,
> >>>> + local->index, flags & MEDIA_LNK_FL_ENABLED);
> >>>> +
> >>>> + if (flags & MEDIA_LNK_FL_ENABLED) {
> >>>> + if (vmux->active == local->index)
> >>>
> >>> Here, you shortcut the mux_control_select_trylock test and return "OK"
> >>> based on a driver-local variable that is intended to keep track of mux
> >>> ownership.
> >>>
> >>>> + return 0;
> >>>> +
> >>>> + if (vmux->active >= 0)
> >>>
> >>> Here too (and this check is not needed, the situation will be covered by
> >>> the mux_control_try_select call).
> >>>
> >>>> + return -EBUSY;
> >>>> +
> >>>> + dev_dbg(sd->dev, "setting %d active\n", local->index);
> >>>> + ret = mux_control_try_select(vmux->mux, local->index);
> >>>> + if (ret < 0)
> >>>> + return ret;
> >>>> + vmux->active = local->index;
> >>>> + } else {
> >>>> + if (vmux->active != local->index)
> >>>> + return 0;
> >>>> +
> >>>> + dev_dbg(sd->dev, "going inactive\n");
> >>>> + mux_control_deselect(vmux->mux);
> >>>
> >>> But here you let go of the mux *before* you clear the driver-local
> >>> ownership indicator. That looks suspicious. My guess is that this is
> >>> "safe" because the upper layers has some serialization, but I don't
> >>> know. Anyway, even if there is something saving you in the upper
> >>> layers, it looks out of order and unneeded. I would have moved the
> >>> below vmux->active = -1; statement up to before the above deselect.
> >>>
> >>> With that fixed, mux usage looks good to me, so you can add an Acked-
> >>> by from me if you wish (goes for the bindings patch as well).
> >>
> >> Ouch, that was a bit too soon. If there is *no* serialization in the
> >> upper layers, this is *not* ok, even with my reordering. There must be
> >> only one call to mux_control_deselect, and w/o serialization there
> >> is a race where you might get multiple deselect calls when several
> >> callers makes it through the active != index check before any of them
> >> manages to set active = -1. That race must be taken care of!
> >
> > Thank you, I've resent a version with a mutex lock around vmux->active.
>
> I had a bunch of ifs in the above message, so I'm not sure it's needed.
> I would expect there to be a lock outside somewhere in the media layer.
> A cursory look gets me to media-entity.c and media_entity_setup_link()
> which does have a mutex. But I'm no media expert, so maybe there are other
> ways of getting to video_mux_link_setup that I'm not aware of?
link_setup is always called under the graph mutex of the /dev/media
device. That is why I didn't think about locking too hard. In fact, I
initially wrote this expecting mux_control_get_exclusive to exist and
the mux select/deselect not to be locked at all.
But set_format is called from an unlocked ioctl on a /dev/v4l-subdev
device. Until your comments I didn't notice that it would be possible to
let link_setup set active = -1 in the middle of the set_format call,
causing it to return garbage.
> If you do end up relying on external locking, a comment saying so would
> be nice. Or even better, some __must_hold markup if possible?
>
> Cheers,
> peda
regards
Philipp
--
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 v6 3/8] i2c: i2c-smbus: add of_i2c_setup_smbus_alert
From: Phil Reid @ 2017-05-03 8:31 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: wsa, robh+dt, mark.rutland, sre, peda, linux-i2c, devicetree,
linux-pm
In-Reply-To: <20170502102013.GB2382@mail.corp.redhat.com>
G'day Benjamin,
On 2/05/2017 18:20, Benjamin Tissoires wrote:
> Hi Phil,
>
> On May 02 2017 or thereabouts, Phil Reid wrote:
>> This commit adds of_i2c_setup_smbus_alert which allows the smbalert
>> driver to be attached to an i2c adapter via the device tree.
>>
>> Signed-off-by: Phil Reid <preid@electromag.com.au>
>> ---
>> Documentation/devicetree/bindings/i2c/i2c.txt | 4 +--
>> drivers/i2c/i2c-smbus.c | 35 +++++++++++++++++++++++++++
>> include/linux/i2c-smbus.h | 9 +++++++
>> 3 files changed, 46 insertions(+), 2 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt
>> index cee9d50..1126398 100644
>> --- a/Documentation/devicetree/bindings/i2c/i2c.txt
>> +++ b/Documentation/devicetree/bindings/i2c/i2c.txt
>> @@ -59,8 +59,8 @@ wants to support one of the below features, it should adapt the bindings below.
>> interrupts used by the device.
>>
>> - interrupt-names
>> - "irq" and "wakeup" names are recognized by I2C core, other names are
>> - left to individual drivers.
>> + "irq", "wakeup" and "smbus_alert" names are recognized by I2C core,
>> + other names are left to individual drivers.
>>
>> - host-notify
>> device uses SMBus host notify protocol instead of interrupt line.
>> diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
>> index df0e2fa..a8f8439 100644
>> --- a/drivers/i2c/i2c-smbus.c
>> +++ b/drivers/i2c/i2c-smbus.c
>> @@ -21,6 +21,7 @@
>> #include <linux/interrupt.h>
>> #include <linux/kernel.h>
>> #include <linux/module.h>
>> +#include <linux/of_irq.h>
>> #include <linux/slab.h>
>> #include <linux/workqueue.h>
>>
>> @@ -238,6 +239,40 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
>> }
>> EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert);
>>
>> +int of_i2c_setup_smbus_alert(struct i2c_adapter *adap)
>> +{
>> + struct i2c_client *client;
>> + struct i2c_smbus_alert_setup *setup;
>> + struct i2c_board_info info = {
>> + I2C_BOARD_INFO("smbus_alert", 0x0c),
>> + };
>
> Shouldn't you use i2c_setup_smbus_alert() instead of manually recreating
> the board_info?
Yes looks like that could be cleaned up I think.
Look at i2c_new_device also it looks like client needs to be kept around and
i2c_unregister_device. I was assuming the apdater would clean it up.
But I'm not sure that's the case when looking again. That makes the change
more invasive into the adpater. would need to add an i2c_client ptr to the
i2c_adapter struct. Thoughts?
> Also i2c_setup_smbus_alert() mentions that the level trigger has to be
> explicitely mentioned, and I can't find if this is the case in your
> patch.
I'm not too sure how this wrks with the device tree / OF stuff.
But initally I had the device tree irq type set incorrectly and things
didn't work. Fixing that up and everything flow thru to the request irq
and was set for the correct mode. So I guessed there was some "magic" in
the core irq code somewhere.
I actually think the thread irq handler should be able to handle everything
and the work queue should disappear. Pretty sure the irq core handles disabling
level interrupts will the threaded handler services things now. But I don't
quite understand how the original code was handle edge vs level trigger irq's.
Not having access to test that I was reluctant to change the hard irq.
Any suggestions helpful...
>
>> + int irq;
>> +
>> + if (!adap->dev.of_node)
>> + return 0;
>> +
>> + irq = of_irq_get_byname(adap->dev.of_node, "smbus_alert");
>> + if (irq == -EINVAL || irq == -ENODATA)
>> + return 0;
>> + else if (irq < 0)
>> + return irq;
>> +
>> + setup = devm_kzalloc(&adap->dev, sizeof(struct i2c_smbus_alert_setup),
>> + GFP_KERNEL);
>
> Problem is i2c-core doesn't use devres at all for now. So the code is
> correct here as it won't segfault but mixing both devres and non-devres
> is error prone.
Ok I look at how to avoid the devm alloc.
>
>> + if (!setup)
>> + return -ENOMEM;
>> +
>> + setup->irq = irq;
>> + info.platform_data = setup;
>> +
>> + client = i2c_new_device(adap, &info);
>> + if (!client)
>> + return -ENODEV;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
>> +
>> /**
>> * i2c_handle_smbus_alert - Handle an SMBus alert
>> * @ara: the ARA client on the relevant adapter
>> diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h
>> index a138502..4732d09 100644
>> --- a/include/linux/i2c-smbus.h
>> +++ b/include/linux/i2c-smbus.h
>> @@ -50,4 +50,13 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
>> struct i2c_smbus_alert_setup *setup);
>> int i2c_handle_smbus_alert(struct i2c_client *ara);
>>
>> +#if IS_ENABLED(CONFIG_I2C_SMBUS)
>> +int of_i2c_setup_smbus_alert(struct i2c_adapter *adap);
>> +#else
>> +static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap)
>> +{
>> + return 0;
>> +}
>> +#endif
>> +
>> #endif /* _LINUX_I2C_SMBUS_H */
>> --
>> 1.8.3.1
>
> Cheers,
> Benjamin
>
>
--
Regards
Phil Reid
^ permalink raw reply
* Re: [PATCH v2 7/7] ARM: dts: imx7d-sdb: Enable PCIe peripheral
From: Shawn Guo @ 2017-05-03 8:30 UTC (permalink / raw)
To: Andrey Smirnov
Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Dong Aisheng, Sascha Hauer,
Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170418150133.31679-8-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Tue, Apr 18, 2017 at 08:01:33AM -0700, Andrey Smirnov wrote:
> Enable PCIe peripheral on this board.
>
> Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
> Cc: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> arch/arm/boot/dts/imx7d-sdb.dts | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/arch/arm/boot/dts/imx7d-sdb.dts b/arch/arm/boot/dts/imx7d-sdb.dts
> index d6f2dda..65dda66 100644
> --- a/arch/arm/boot/dts/imx7d-sdb.dts
> +++ b/arch/arm/boot/dts/imx7d-sdb.dts
> @@ -349,6 +349,12 @@
> };
> };
>
> +&pcie {
> + pinctrl-names = "default";
No corresponding pinctrl-0 entry? In that case, there is no point to
have pinctrl-names.
Shawn
> + reset-gpio = <&extended_io 1 GPIO_ACTIVE_LOW>;
> + status = "okay";
> +};
> +
> &pwm1 {
> pinctrl-names = "default";
> pinctrl-0 = <&pinctrl_pwm1>;
> --
> 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 6/7] ARM: dts: imx7d: Add node for PCIe controller
From: Shawn Guo @ 2017-05-03 8:28 UTC (permalink / raw)
To: Andrey Smirnov
Cc: yurovsky, Dong Aisheng, Sascha Hauer, Fabio Estevam, Rob Herring,
Mark Rutland, Russell King, devicetree, linux-kernel,
linux-arm-kernel
In-Reply-To: <20170418150133.31679-7-andrew.smirnov@gmail.com>
On Tue, Apr 18, 2017 at 08:01:32AM -0700, Andrey Smirnov wrote:
> Cc: yurovsky@gmail.com
> Cc: Dong Aisheng <aisheng.dong@nxp.com>
> Cc: Shawn Guo <shawnguo@kernel.org>
> Cc: Sascha Hauer <kernel@pengutronix.de>
> Cc: Fabio Estevam <fabio.estevam@nxp.com>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Russell King <linux@armlinux.org.uk>
> Cc: devicetree@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
> arch/arm/boot/dts/imx7d.dtsi | 37 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
>
> diff --git a/arch/arm/boot/dts/imx7d.dtsi b/arch/arm/boot/dts/imx7d.dtsi
> index f6dee41..f46814a 100644
> --- a/arch/arm/boot/dts/imx7d.dtsi
> +++ b/arch/arm/boot/dts/imx7d.dtsi
> @@ -42,6 +42,7 @@
> */
>
> #include "imx7s.dtsi"
> +#include <dt-bindings/reset/imx7-reset.h>
It has dependency on imx7 reset driver, and I need to wait for
v4.12-rc1 to apply the patch.
Shawn
^ permalink raw reply
* Re: [PATCH v2 3/7] ARM: dts: imx7s: Add node for GPC
From: Shawn Guo @ 2017-05-03 8:26 UTC (permalink / raw)
To: Andrey Smirnov
Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w, Dong Aisheng, Sascha Hauer,
Fabio Estevam, Rob Herring, Mark Rutland, Russell King,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20170418150133.31679-4-andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Tue, Apr 18, 2017 at 08:01:29AM -0700, Andrey Smirnov wrote:
> Add node for GPC and specify as a parent interrupt controller for SoC bus.
>
> Cc: yurovsky-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
> Cc: Dong Aisheng <aisheng.dong-3arQi8VN3Tc@public.gmane.org>
> Cc: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Sascha Hauer <kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Cc: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
> Cc: Russell King <linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org>
> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Signed-off-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> arch/arm/boot/dts/imx7s.dtsi | 26 +++++++++++++++++++++++++-
> 1 file changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
> index 7148eac..5ba1289 100644
> --- a/arch/arm/boot/dts/imx7s.dtsi
> +++ b/arch/arm/boot/dts/imx7s.dtsi
> @@ -42,6 +42,7 @@
> */
>
> #include <dt-bindings/clock/imx7d-clock.h>
> +#include <dt-bindings/power/imx7-power.h>
It has dependency on imx7 power domain driver, and I need to wait for
v4.12-rc1 to apply the patch.
Shawn
> #include <dt-bindings/gpio/gpio.h>
> #include <dt-bindings/input/input.h>
> #include <dt-bindings/interrupt-controller/arm-gic.h>
--
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
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