Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH RFC 2/6] dt-bindings: hwmon: Add Raspberry Pi voltage sensor
From: Guenter Roeck @ 2018-05-16 14:52 UTC (permalink / raw)
  To: Stefan Wahren
  Cc: Mark Rutland, devicetree, Jean Delvare, Scott Branden, Ray Jui,
	Phil Elwell, Eric Anholt, Rob Herring, bcm-kernel-feedback-list,
	linux-rpi-kernel, Florian Fainelli, linux-hwmon, linux-arm-kernel
In-Reply-To: <1526477827-10859-3-git-send-email-stefan.wahren@i2se.com>

On Wed, May 16, 2018 at 03:37:03PM +0200, Stefan Wahren wrote:
> Recent VC4 firmware provides a hardware-independent way to retrieve the
> under-voltage sensor on the following Raspberry Pi boards:
>   - Raspberry Pi B+ (via GPIO on SoC)
>   - Raspberry Pi A+ (via GPIO on SoC)
>   - Raspberry Pi 2 B (via GPIO on SoC)
>   - Raspberry Pi 3 B (via GPIO on port expander)
>   - Raspberry Pi 3 B+ (via PMIC)
> 
> TODO:
>   - try to make bcm2835 firmware a bus driver
> 
> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
> ---
>  .../devicetree/bindings/hwmon/raspberrypi-hwmon.txt   | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
> 
> diff --git a/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt b/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
> new file mode 100644
> index 0000000..ec2523f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
> @@ -0,0 +1,19 @@
> +Raspberry Pi voltage sensor
> +---------------------------
> +
> +The VC4 firmware exposes a mailbox interface that allows the ARM core
> +to access the board's voltage sensor.
> +
> +Required properties :
> +- compatible : Should be "raspberrypi,bcm2835-hwmon"

'hwmon' is very Linux specific. DT bindings are supposed to be
OS agnostic.

Guenter

> +
> +Example:
> +
> +firmware: firmware-rpi {
> +	compatible = "raspberrypi,bcm2835-firmware";
> +	mboxes = <&mailbox>;
> +
> +	voltage-sensor {
> +		compatible = "raspberrypi,bcm2835-hwmon";
> +	};
> +};
> -- 
> 2.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-hwmon" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
From: Jacob Chen @ 2018-05-16 14:39 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: open list:ARM/Rockchip SoC..., linux-kernel, linux-arm-kernel,
	Mauro Carvalho Chehab, Linux Media Mailing List, Sakari Ailus,
	Hans Verkuil, Tomasz Figa, Shunqian Zheng,
	钟以崇, Eddie Cai, Jeffy Chen, devicetree,
	Heiko Stuebner, Xing Zheng
In-Reply-To: <18647985.GIavVuIZz4@avalon>

Hi Laurent,

2018-05-16 13:20 GMT+08:00 Laurent Pinchart <laurent.pinchart@ideasonboard.com>:
> Hi Jacob,
>
> Thank you for the patch.
>
> On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
>> From: Jacob Chen <jacob2.chen@rock-chips.com>
>>
>> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
>
> Should this really be a subdev driver ? After a quick look at the code, the
> only parameters you need to configure the PHY is the number of lanes and the
> data rate. Implementing the whole subdev API seems overcomplicated to me,
> especially given that the D-PHY doesn't deal with video streams as such, but
> operates one level down. Shouldn't we model the D-PHY using the Linux PHY
> framework ? I believe all the features you need are there except for a D-PHY-
> specific configuration function that should be very easy to add.
>

It deserves a subdev driver since the ISP is not the only user.
Other driver, like VIP, use it too.


>> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
>> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
>> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
>> ---
>>  .../media/platform/rockchip/isp1/mipi_dphy_sy.c    | 868 ++++++++++++++++++
>>  .../media/platform/rockchip/isp1/mipi_dphy_sy.h    |  15 +
>>  2 files changed, 883 insertions(+)
>>  create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>>  create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>
>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c new file mode 100644
>> index 000000000000..32140960557a
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> @@ -0,0 +1,868 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Rockchip MIPI Synopsys DPHY driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regmap.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-fwnode.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#define RK3288_GRF_SOC_CON6  0x025c
>> +#define RK3288_GRF_SOC_CON8  0x0264
>> +#define RK3288_GRF_SOC_CON9  0x0268
>> +#define RK3288_GRF_SOC_CON10 0x026c
>> +#define RK3288_GRF_SOC_CON14 0x027c
>> +#define RK3288_GRF_SOC_STATUS21      0x02d4
>> +#define RK3288_GRF_IO_VSEL   0x0380
>> +#define RK3288_GRF_SOC_CON15 0x03a4
>> +
>> +#define RK3399_GRF_SOC_CON9  0x6224
>> +#define RK3399_GRF_SOC_CON21 0x6254
>> +#define RK3399_GRF_SOC_CON22 0x6258
>> +#define RK3399_GRF_SOC_CON23 0x625c
>> +#define RK3399_GRF_SOC_CON24 0x6260
>> +#define RK3399_GRF_SOC_CON25 0x6264
>> +#define RK3399_GRF_SOC_STATUS1       0xe2a4
>> +
>> +#define CLOCK_LANE_HS_RX_CONTROL             0x34
>> +#define LANE0_HS_RX_CONTROL                  0x44
>> +#define LANE1_HS_RX_CONTROL                  0x54
>> +#define LANE2_HS_RX_CONTROL                  0x84
>> +#define LANE3_HS_RX_CONTROL                  0x94
>> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL  0x75
>> +
>> +/*
>> + * CSI HOST
>> + */
>> +#define CSIHOST_PHY_TEST_CTRL0               0x30
>> +#define CSIHOST_PHY_TEST_CTRL1               0x34
>> +#define CSIHOST_PHY_SHUTDOWNZ                0x08
>> +#define CSIHOST_DPHY_RSTZ            0x0c
>> +
>> +#define PHY_TESTEN_ADDR                      (0x1 << 16)
>> +#define PHY_TESTEN_DATA                      (0x0 << 16)
>> +#define PHY_TESTCLK                  (0x1 << 1)
>> +#define PHY_TESTCLR                  (0x1 << 0)
>> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
>> +
>> +#define HIWORD_UPDATE(val, mask, shift) \
>> +     ((val) << (shift) | (mask) << ((shift) + 16))
>> +
>> +enum mipi_dphy_sy_pads {
>> +     MIPI_DPHY_SY_PAD_SINK = 0,
>> +     MIPI_DPHY_SY_PAD_SOURCE,
>> +     MIPI_DPHY_SY_PADS_NUM,
>> +};
>> +
>> +enum dphy_reg_id {
>> +     GRF_DPHY_RX0_TURNDISABLE = 0,
>> +     GRF_DPHY_RX0_FORCERXMODE,
>> +     GRF_DPHY_RX0_FORCETXSTOPMODE,
>> +     GRF_DPHY_RX0_ENABLE,
>> +     GRF_DPHY_RX0_TESTCLR,
>> +     GRF_DPHY_RX0_TESTCLK,
>> +     GRF_DPHY_RX0_TESTEN,
>> +     GRF_DPHY_RX0_TESTDIN,
>> +     GRF_DPHY_RX0_TURNREQUEST,
>> +     GRF_DPHY_RX0_TESTDOUT,
>> +     GRF_DPHY_TX0_TURNDISABLE,
>> +     GRF_DPHY_TX0_FORCERXMODE,
>> +     GRF_DPHY_TX0_FORCETXSTOPMODE,
>> +     GRF_DPHY_TX0_TURNREQUEST,
>> +     GRF_DPHY_TX1RX1_TURNDISABLE,
>> +     GRF_DPHY_TX1RX1_FORCERXMODE,
>> +     GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
>> +     GRF_DPHY_TX1RX1_ENABLE,
>> +     GRF_DPHY_TX1RX1_MASTERSLAVEZ,
>> +     GRF_DPHY_TX1RX1_BASEDIR,
>> +     GRF_DPHY_TX1RX1_ENABLECLK,
>> +     GRF_DPHY_TX1RX1_TURNREQUEST,
>> +     GRF_DPHY_RX1_SRC_SEL,
>> +     /* rk3288 only */
>> +     GRF_CON_DISABLE_ISP,
>> +     GRF_CON_ISP_DPHY_SEL,
>> +     GRF_DSI_CSI_TESTBUS_SEL,
>> +     GRF_DVP_V18SEL,
>> +     /* below is for rk3399 only */
>> +     GRF_DPHY_RX0_CLK_INV_SEL,
>> +     GRF_DPHY_RX1_CLK_INV_SEL,
>> +};
>> +
>> +struct dphy_reg {
>> +     u32 offset;
>> +     u32 mask;
>> +     u32 shift;
>> +};
>> +
>> +#define PHY_REG(_offset, _width, _shift) \
>> +     { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
>> +
>> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
>> +     [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
>> +     [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
>> +     [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
>> +     [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
>> +     [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
>> +     [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
>> +     [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
>> +     [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
>> +     [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
>> +     [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
>> +     [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
>> +     [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
>> +     [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
>> +     [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
>> +     [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
>> +     [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
>> +     [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
>> +     [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
>> +     [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
>> +     [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
>> +     [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
>> +     [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
>> +     [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
>> +     [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
>> +     [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
>> +};
>> +
>> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
>> +     [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
>> +     [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
>> +     [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
>> +     [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
>> +     [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
>> +     [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
>> +     [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
>> +     [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
>> +     [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
>> +     [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
>> +     [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
>> +     [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
>> +     [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
>> +     [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
>> +     [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
>> +     [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
>> +     [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
>> +     [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
>> +     [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
>> +     [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
>> +     [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
>> +     [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
>> +     [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
>> +     [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
>> +     [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
>> +     [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
>> +     [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
>> +};
>> +
>> +struct hsfreq_range {
>> +     u32 range_h;
>> +     u8 cfg_bit;
>> +};
>> +
>> +struct mipidphy_priv;
>> +
>> +struct dphy_drv_data {
>> +     const char * const *clks;
>> +     int num_clks;
>> +     const struct hsfreq_range *hsfreq_ranges;
>> +     int num_hsfreq_ranges;
>> +     const struct dphy_reg *regs;
>> +};
>> +
>> +struct sensor_async_subdev {
>> +     struct v4l2_async_subdev asd;
>> +     struct v4l2_mbus_config mbus;
>> +     int lanes;
>> +};
>> +
>> +#define MAX_DPHY_CLK         8
>> +#define MAX_DPHY_SENSORS     2
>> +
>> +struct mipidphy_sensor {
>> +     struct v4l2_subdev *sd;
>> +     struct v4l2_mbus_config mbus;
>> +     int lanes;
>> +};
>> +
>> +struct mipidphy_priv {
>> +     struct device *dev;
>> +     struct regmap *regmap_grf;
>> +     const struct dphy_reg *grf_regs;
>> +     struct clk *clks[MAX_DPHY_CLK];
>> +     const struct dphy_drv_data *drv_data;
>> +     u64 data_rate_mbps;
>> +     struct v4l2_async_notifier notifier;
>> +     struct v4l2_subdev sd;
>> +     struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
>> +     struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
>> +     int num_sensors;
>> +     bool is_streaming;
>> +     void __iomem *txrx_base_addr;
>> +     int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
>> +};
>> +
>> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev
>> *subdev) +{
>> +     return container_of(subdev, struct mipidphy_priv, sd);
>> +}
>> +
>> +static inline void write_grf_reg(struct mipidphy_priv *priv,
>> +                              int index, u8 value)
>> +{
>> +     const struct dphy_reg *reg = &priv->grf_regs[index];
>> +     unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
>> +
>> +     WARN_ON(!reg->offset);
>> +     regmap_write(priv->regmap_grf, reg->offset, val);
>> +}
>> +
>> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
>> +                          u8 test_code, u8 test_data)
>> +{
>> +     /*
>> +      * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
>> +      * is latched internally as the current test code. Test data is
>> +      * programmed internally by rising edge on TESTCLK.
>> +      */
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> +}
>> +
>> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char
>> addr, +                            unsigned char data)
>> +{
>> +     /*
>> +      * TESTEN =1,TESTDIN=addr
>> +      * TESTCLK=0
>> +      * TESTEN =0,TESTDIN=data
>> +      * TESTCLK=1
>> +      */
>> +     writel((PHY_TESTEN_ADDR | addr),
>> +            priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>> +     writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> +     writel((PHY_TESTEN_DATA | data),
>> +            priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>> +     writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> +}
>> +
>> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
>> +{
>> +     struct media_pad *local, *remote;
>> +     struct media_entity *sensor_me;
>> +
>> +     local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
>> +     remote = media_entity_remote_pad(local);
>> +     if (!remote) {
>> +             v4l2_warn(sd, "No link between dphy and sensor\n");
>> +             return NULL;
>> +     }
>> +
>> +     sensor_me = media_entity_remote_pad(local)->entity;
>> +     return media_entity_to_v4l2_subdev(sensor_me);
>> +}
>> +
>> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
>> +                                         struct v4l2_subdev *sd)
>> +{
>> +     int i;
>> +
>> +     for (i = 0; i < priv->num_sensors; ++i)
>> +             if (priv->sensors[i].sd == sd)
>> +                     return &priv->sensors[i];
>> +
>> +     return NULL;
>> +}
>> +
>> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
>> +{
>> +     struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +     struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> +     struct v4l2_ctrl *link_freq;
>> +     struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
>> +     int ret;
>> +
>> +     link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
>> +     if (!link_freq) {
>> +             v4l2_warn(sd, "No pixel rate control in subdev\n");
>> +             return -EPIPE;
>> +     }
>> +
>> +     qm.index = v4l2_ctrl_g_ctrl(link_freq);
>> +     ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
>> +     if (ret < 0) {
>> +             v4l2_err(sd, "Failed to get menu item\n");
>> +             return ret;
>> +     }
>> +
>> +     if (!qm.value) {
>> +             v4l2_err(sd, "Invalid link_freq\n");
>> +             return -EINVAL;
>> +     }
>> +     priv->data_rate_mbps = qm.value * 2;
>> +     do_div(priv->data_rate_mbps, 1000 * 1000);
>> +
>> +     return 0;
>> +}
>> +
>> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
>> +{
>> +     struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +     int  ret = 0;
>> +
>> +     if (priv->is_streaming)
>> +             return 0;
>> +
>> +     ret = mipidphy_get_sensor_data_rate(sd);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     priv->stream_on(priv, sd);
>> +
>> +     priv->is_streaming = true;
>> +
>> +     return 0;
>> +}
>> +
>> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
>> +{
>> +     struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +
>> +     priv->is_streaming = false;
>> +
>> +     return 0;
>> +}
>> +
>> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
>> +{
>> +     if (on)
>> +             return mipidphy_s_stream_start(sd);
>> +     else
>> +             return mipidphy_s_stream_stop(sd);
>> +}
>> +
>> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
>> +                               struct v4l2_mbus_config *config)
>> +{
>> +     struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +     struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> +     struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> +
>> +     *config = sensor->mbus;
>> +
>> +     return 0;
>> +}
>> +
>> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
>> +{
>> +     struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +
>> +     if (on)
>> +             return pm_runtime_get_sync(priv->dev);
>> +     else
>> +             return pm_runtime_put(priv->dev);
>> +}
>> +
>> +static int mipidphy_runtime_suspend(struct device *dev)
>> +{
>> +     struct media_entity *me = dev_get_drvdata(dev);
>> +     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> +     struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +     int i, num_clks;
>> +
>> +     num_clks = priv->drv_data->num_clks;
>> +     for (i = num_clks - 1; i >= 0; i--)
>> +             clk_disable_unprepare(priv->clks[i]);
>> +
>> +     return 0;
>> +}
>> +
>> +static int mipidphy_runtime_resume(struct device *dev)
>> +{
>> +     struct media_entity *me = dev_get_drvdata(dev);
>> +     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> +     struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +     int i, num_clks, ret;
>> +
>> +     num_clks = priv->drv_data->num_clks;
>> +     for (i = 0; i < num_clks; i++) {
>> +             ret = clk_prepare_enable(priv->clks[i]);
>> +             if (ret < 0)
>> +                     goto err;
>> +     }
>> +
>> +     return 0;
>> +err:
>> +     while (--i >= 0)
>> +             clk_disable_unprepare(priv->clks[i]);
>> +     return ret;
>> +}
>> +
>> +/* dphy accepts all fmt/size from sensor */
>> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
>> +                             struct v4l2_subdev_pad_config *cfg,
>> +                             struct v4l2_subdev_format *fmt)
>> +{
>> +     struct v4l2_subdev *sensor = get_remote_sensor(sd);
>> +
>> +     /*
>> +      * Do not allow format changes and just relay whatever
>> +      * set currently in the sensor.
>> +      */
>> +     return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
>> +}
>> +
>> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
>> +     .set_fmt = mipidphy_get_set_fmt,
>> +     .get_fmt = mipidphy_get_set_fmt,
>> +};
>> +
>> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
>> +     .s_power = mipidphy_s_power,
>> +};
>> +
>> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
>> +     .g_mbus_config = mipidphy_g_mbus_config,
>> +     .s_stream = mipidphy_s_stream,
>> +};
>> +
>> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
>> +     .core = &mipidphy_core_ops,
>> +     .video = &mipidphy_video_ops,
>> +     .pad = &mipidphy_subdev_pad_ops,
>> +};
>> +
>> +/* These tables must be sorted by .range_h ascending. */
>> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
>> +     {  89, 0x00}, {  99, 0x10}, { 109, 0x20}, { 129, 0x01},
>> +     { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>> +     { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>> +     { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>> +     { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>> +     { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>> +     { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>> +     { 999, 0x1a}
>> +};
>> +
>> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
>> +     {  89, 0x00}, {  99, 0x10}, { 109, 0x20}, { 129, 0x01},
>> +     { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>> +     { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>> +     { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>> +     { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>> +     { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>> +     { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>> +     { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
>> +     {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
>> +     {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
>> +};
>> +
>> +static const char * const rk3399_mipidphy_clks[] = {
>> +     "dphy-ref",
>> +     "dphy-cfg",
>> +     "grf",
>> +};
>> +
>> +static const char * const rk3288_mipidphy_clks[] = {
>> +     "dphy-ref",
>> +     "pclk",
>> +};
>> +
>> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
>> +                              struct v4l2_subdev *sd)
>> +{
>> +     struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> +     struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> +     const struct dphy_drv_data *drv_data = priv->drv_data;
>> +     const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>> +     int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>> +     int i, hsfreq = 0;
>> +
>> +     for (i = 0; i < num_hsfreq_ranges; i++) {
>> +             if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>> +                     hsfreq = hsfreq_ranges[i].cfg_bit;
>> +                     break;
>> +             }
>> +     }
>> +     write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
>> +     /* Disable lan turn around, which is ignored in receive mode */
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
>> +
>> +     write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
>> +
>> +     /* dphy start */
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
>> +     usleep_range(100, 150);
>> +     write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
>> +     usleep_range(100, 150);
>> +
>> +     /* set clock lane */
>> +     /* HS hsfreq_range & lane 0  settle bypass */
>> +     mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>> +     /* HS RX Control of lane0 */
>> +     mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>> +     /* HS RX Control of lane1 */
>> +     mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>> +     /* HS RX Control of lane2 */
>> +     mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>> +     /* HS RX Control of lane3 */
>> +     mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>> +     /* HS RX Data Lanes Settle State Time Control */
>> +     mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>> +                      THS_SETTLE_COUNTER_THRESHOLD);
>> +
>> +     /* Normal operation */
>> +     mipidphy0_wr_reg(priv, 0x0, 0);
>> +
>> +     return 0;
>> +}
>> +
>> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
>> +                                struct v4l2_subdev *sd)
>> +{
>> +     struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> +     struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> +     const struct dphy_drv_data *drv_data = priv->drv_data;
>> +     const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>> +     int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>> +     int i, hsfreq = 0;
>> +
>> +     for (i = 0; i < num_hsfreq_ranges; i++) {
>> +             if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>> +                     hsfreq = hsfreq_ranges[i].cfg_bit;
>> +                     break;
>> +             }
>> +     }
>> +     write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
>> +     write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
>> +     write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
>> +     write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
>> +     write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
>> +     /* Disable lan turn around, which is ignored in receive mode */
>> +     write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
>> +     write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
>> +     write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
>> +     write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
>> +     write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
>> +                   GENMASK(sensor->lanes - 1, 0));
>> +     /* dphy start */
>> +     writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
>> +     writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
>> +     writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> +     writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> +     usleep_range(100, 150);
>> +     writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> +     usleep_range(100, 150);
>> +
>> +     /* set clock lane */
>> +     mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>> +     mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>> +     mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>> +     mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>> +     mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>> +     /* HS RX Data Lanes Settle State Time Control */
>> +     mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>> +                      THS_SETTLE_COUNTER_THRESHOLD);
>> +
>> +     /* Normal operation */
>> +     mipidphy1_wr_reg(priv, 0x0, 0);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
>> +     .clks = rk3288_mipidphy_clks,
>> +     .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
>> +     .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
>> +     .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
>> +     .regs = rk3288_grf_dphy_regs,
>> +};
>> +
>> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
>> +     .clks = rk3399_mipidphy_clks,
>> +     .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
>> +     .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
>> +     .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
>> +     .regs = rk3399_grf_dphy_regs,
>> +};
>> +
>> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
>> +     {
>> +             .compatible = "rockchip,rk3399-mipi-dphy",
>> +             .data = &rk3399_mipidphy_drv_data,
>> +     },
>> +     {
>> +             .compatible = "rockchip,rk3288-mipi-dphy",
>> +             .data = &rk3288_mipidphy_drv_data,
>> +     },
>> +     {}
>> +};
>> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
>> +
>> +/* The .bound() notifier callback when a match is found */
>> +static int
>> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
>> +                              struct v4l2_subdev *sd,
>> +                              struct v4l2_async_subdev *asd)
>> +{
>> +     struct mipidphy_priv *priv = container_of(notifier,
>> +                                               struct mipidphy_priv,
>> +                                               notifier);
>> +     struct sensor_async_subdev *s_asd = container_of(asd,
>> +                                     struct sensor_async_subdev, asd);
>> +     struct mipidphy_sensor *sensor;
>> +     unsigned int pad, ret;
>> +
>> +     if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
>> +             return -EBUSY;
>> +
>> +     sensor = &priv->sensors[priv->num_sensors++];
>> +     sensor->lanes = s_asd->lanes;
>> +     sensor->mbus = s_asd->mbus;
>> +     sensor->sd = sd;
>> +
>> +     for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
>> +             if (sensor->sd->entity.pads[pad].flags
>> +                                     & MEDIA_PAD_FL_SOURCE)
>> +                     break;
>> +
>> +     if (pad == sensor->sd->entity.num_pads) {
>> +             dev_err(priv->dev,
>> +                     "failed to find src pad for %s\n",
>> +                     sensor->sd->name);
>> +
>> +             return -ENXIO;
>> +     }
>> +
>> +     ret = media_create_pad_link(
>> +                     &sensor->sd->entity, pad,
>> +                     &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
>> +                     priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
>> +     if (ret) {
>> +             dev_err(priv->dev,
>> +                     "failed to create link for %s\n",
>> +                     sensor->sd->name);
>> +             return ret;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/* The .unbind callback */
>> +static void
>> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
>> +                               struct v4l2_subdev *sd,
>> +                               struct v4l2_async_subdev *asd)
>> +{
>> +     struct mipidphy_priv *priv = container_of(notifier,
>> +                                               struct mipidphy_priv,
>> +                                               notifier);
>> +     struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
>> +
>> +     sensor->sd = NULL;
>> +}
>> +
>> +static const struct
>> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
>> +     .bound = rockchip_mipidphy_notifier_bound,
>> +     .unbind = rockchip_mipidphy_notifier_unbind,
>> +};
>> +
>> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
>> +                                       struct v4l2_fwnode_endpoint *vep,
>> +                                       struct v4l2_async_subdev *asd)
>> +{
>> +     struct sensor_async_subdev *s_asd =
>> +                     container_of(asd, struct sensor_async_subdev, asd);
>> +     struct v4l2_mbus_config *config = &s_asd->mbus;
>> +
>> +     if (vep->bus_type != V4L2_MBUS_CSI2) {
>> +             dev_err(dev, "Only CSI2 bus type is currently supported\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (vep->base.port != 0) {
>> +             dev_err(dev, "The PHY has only port 0\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     config->type = V4L2_MBUS_CSI2;
>> +     config->flags = vep->bus.mipi_csi2.flags;
>> +     s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
>> +
>> +     switch (vep->bus.mipi_csi2.num_data_lanes) {
>> +     case 1:
>> +             config->flags |= V4L2_MBUS_CSI2_1_LANE;
>> +             break;
>> +     case 2:
>> +             config->flags |= V4L2_MBUS_CSI2_2_LANE;
>> +             break;
>> +     case 3:
>> +             config->flags |= V4L2_MBUS_CSI2_3_LANE;
>> +             break;
>> +     case 4:
>> +             config->flags |= V4L2_MBUS_CSI2_4_LANE;
>> +             break;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
>> +{
>> +     int ret;
>> +
>> +     priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
>> +             MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
>> +     priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
>> +             MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
>> +
>> +     ret = media_entity_pads_init(&priv->sd.entity,
>> +                             MIPI_DPHY_SY_PADS_NUM, priv->pads);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
>> +             priv->dev, &priv->notifier,
>> +             sizeof(struct sensor_async_subdev), 0,
>> +             rockchip_mipidphy_fwnode_parse);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     if (!priv->notifier.num_subdevs)
>> +             return -ENODEV; /* no endpoint */
>> +
>> +     priv->sd.subdev_notifier = &priv->notifier;
>> +     priv->notifier.ops = &rockchip_mipidphy_async_ops;
>> +     ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
>> +     if (ret) {
>> +             dev_err(priv->dev,
>> +                     "failed to register async notifier : %d\n", ret);
>> +             v4l2_async_notifier_cleanup(&priv->notifier);
>> +             return ret;
>> +     }
>> +
>> +     return v4l2_async_register_subdev(&priv->sd);
>> +}
>> +
>> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct v4l2_subdev *sd;
>> +     struct mipidphy_priv *priv;
>> +     struct regmap *grf;
>> +     struct resource *res;
>> +     const struct of_device_id *of_id;
>> +     const struct dphy_drv_data *drv_data;
>> +     int i, ret;
>> +
>> +     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> +     if (!priv)
>> +             return -ENOMEM;
>> +     priv->dev = dev;
>> +
>> +     of_id = of_match_device(rockchip_mipidphy_match_id, dev);
>> +     if (!of_id)
>> +             return -EINVAL;
>> +
>> +     grf = syscon_node_to_regmap(dev->parent->of_node);
>> +     if (IS_ERR(grf)) {
>> +             grf = syscon_regmap_lookup_by_phandle(dev->of_node,
>> +                                                   "rockchip,grf");
>> +             if (IS_ERR(grf)) {
>> +                     dev_err(dev, "Can't find GRF syscon\n");
>> +                     return -ENODEV;
>> +             }
>> +     }
>> +     priv->regmap_grf = grf;
>> +
>> +     drv_data = of_id->data;
>> +     for (i = 0; i < drv_data->num_clks; i++) {
>> +             priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
>> +
>> +             if (IS_ERR(priv->clks[i])) {
>> +                     dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
>> +                     return PTR_ERR(priv->clks[i]);
>> +             }
>> +     }
>> +
>> +     priv->grf_regs = drv_data->regs;
>> +     priv->drv_data = drv_data;
>> +     priv->stream_on = mipidphy_txrx_stream_on;
>> +     priv->txrx_base_addr = NULL;
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     priv->txrx_base_addr = devm_ioremap_resource(dev, res);
>> +     if (IS_ERR(priv->txrx_base_addr))
>> +             priv->stream_on = mipidphy_rx_stream_on;
>> +
>> +     sd = &priv->sd;
>> +     v4l2_subdev_init(sd, &mipidphy_subdev_ops);
>> +     sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +     snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
>> +     sd->dev = dev;
>> +
>> +     platform_set_drvdata(pdev, &sd->entity);
>> +
>> +     ret = rockchip_mipidphy_media_init(priv);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     pm_runtime_enable(&pdev->dev);
>> +
>> +     return 0;
>> +}
>> +
>> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
>> +{
>> +     struct media_entity *me = platform_get_drvdata(pdev);
>> +     struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> +
>> +     media_entity_cleanup(&sd->entity);
>> +
>> +     pm_runtime_disable(&pdev->dev);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
>> +     SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
>> +                        mipidphy_runtime_resume, NULL)
>> +};
>> +
>> +static struct platform_driver rockchip_isp_mipidphy_driver = {
>> +     .probe = rockchip_mipidphy_probe,
>> +     .remove = rockchip_mipidphy_remove,
>> +     .driver = {
>> +                     .name = "rockchip-sy-mipi-dphy",
>> +                     .pm = &rockchip_mipidphy_pm_ops,
>> +                     .of_match_table = rockchip_mipidphy_match_id,
>> +     },
>> +};
>> +
>> +module_platform_driver(rockchip_isp_mipidphy_driver);
>> +MODULE_AUTHOR("Rockchip Camera/ISP team");
>> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
>> +MODULE_LICENSE("Dual BSD/GPL");
>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h new file mode 100644
>> index 000000000000..c558791064a2
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>> @@ -0,0 +1,15 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Rockchip MIPI Synopsys DPHY driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#ifndef __MIPI_DPHY_SY_H__
>> +#define __MIPI_DPHY_SY_H__
>> +
>> +#include <media/v4l2-subdev.h>
>> +
>> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
>> +
>> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
>
>
> --
> Regards,
>
> Laurent Pinchart
>
>
>

^ permalink raw reply

* Re: [RFC PATCH] driver core: make deferring probe forever optional
From: Linus Walleij @ 2018-05-16 14:38 UTC (permalink / raw)
  To: Michal Simek
  Cc: Alexander Graf, Bjorn Andersson, Rob Herring,
	linux-kernel@vger.kernel.org,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Greg Kroah-Hartman, Grant Likely, Mark Brown, Stephen Boyd,
	boot-architecture, Linux ARM, Jan Kiszka
In-Reply-To: <45eca00d-57f5-21b5-1f6e-b5f3f60ad276@xilinx.com>

On Mon, May 14, 2018 at 2:44 PM, Michal Simek <michal.simek@xilinx.com> wrote:
> On 14.5.2018 09:37, Alexander Graf wrote:
>> On 05/14/2018 12:01 AM, Linus Walleij wrote:
>>> On Wed, May 9, 2018 at 11:44 AM, Alexander Graf <agraf@suse.de> wrote:
>>>> On 05/07/2018 08:31 PM, Bjorn Andersson wrote:
>>>>> Can you please name platform that has enough support for Alexander to
>>>>> care about backwards and forwards compatibility but lacks a pinctrl
>>>>> driver.
>>>> ZynqMP is one example that immediately comes to my mind. I'm sure
>>>> there are
>>>> others too.
>>> Why isn't that using drivers/pinctrl/pinctrl-zynq.c?
>>>
>>> How is it so very different from (old) Zynq as it is already using
>>> the same GPIO driver?
>>
>> That one is very simple: ZynqMP is usually an AMP system where Linux
>> doesn't have full knowledge of the overall system. IIUC they have a tiny
>> microblaze (PMU) that does the actual full system configuration for
>> peripherals that may interfere with each other. This architecture also
>> allows for safety critical code to run alongside a (less safe) Linux
>> system.
>
> Linux is running in non secure world that's why Linux can't have full
> system visibility and Linux should ask firmware. It doesn't matter if
> firmware is running on specific unit or just secure SW in EL3/EL1-S, EL0-S.
> You can also configure ZynqMP to protect these address ranges not to be
> accessible from NS sw.
> If you don't care about security you can use normal read/write accesses
> at least for gpio case. Pinctrl/clk will be driven via firmware interface.

OK I get it, the situation is similar to some ACPI-based BIOSes on
PC where one needs to ask the firmware for misc services.

What would be nice is to standardize these APIs (like ACPI or device
tree does) so we don't end up with one per-SoC-specific driver per
system. But I guess it is not my pick.

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH v5 0/7] pcal6524 extensions and fixes for pca953x driver
From: H. Nikolaus Schaller @ 2018-05-16 14:37 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Kumar Gala, Andy Shevchenko, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Alexandre Courbot,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list:GPIO SUBSYSTEM, linux-kernel@vger.kernel.org,
	Discussions about the Letux Kernel, kernel
In-Reply-To: <CACRpkda_nrL8HQL3y+tvHKff+5zKykd3Ym1waEm=JC1tFQbbcw@mail.gmail.com>


> Am 16.05.2018 um 16:31 schrieb Linus Walleij <linus.walleij@linaro.org>:
> 
> On Wed, May 16, 2018 at 3:32 PM, H. Nikolaus Schaller <hns@goldelico.com> wrote:
>>> Am 16.05.2018 um 13:53 schrieb Linus Walleij <linus.walleij@linaro.org>:
>>> On Sat, Apr 28, 2018 at 6:31 PM, H. Nikolaus Schaller <hns@goldelico.com> wrote:
>>> 
>>>> V5:
>>>> * fix wrong split up between patches 1/7and 2/7.
>>> 
>>> I applied patches 1, 2, 3 so we get some movement on the patch
>>> set and not too much for you to rebase.
>> 
>> thanks!
>> 
>> Well, I already had edited the commit messages for resending...
>> 
>>> 
>>> It's fine to just resend the rest next time.
>> 
>> There is only one point open before resending:
>> 
>> what is the preferred style to be used for PCAL_GPIO_MASK?
>> 
>> * GENMASK(4, 0)
>> * or 0x1f
> 
> No strong opinion... sorry. Whatever you & the other driver
> contributors are most convenient with.

Ok, then let's keep the GENMASK(4, 0) since nobody did complain
about it.

BR and thanks,
Nikolaus

^ permalink raw reply

* Re: [PATCH v5 0/7] pcal6524 extensions and fixes for pca953x driver
From: Linus Walleij @ 2018-05-16 14:31 UTC (permalink / raw)
  To: H. Nikolaus Schaller
  Cc: Kumar Gala, Andy Shevchenko, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Alexandre Courbot,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list:GPIO SUBSYSTEM, linux-kernel@vger.kernel.org,
	Discussions about the Letux Kernel, kernel
In-Reply-To: <6AA622D1-DA8C-4C58-8EAE-555989E69149@goldelico.com>

On Wed, May 16, 2018 at 3:32 PM, H. Nikolaus Schaller <hns@goldelico.com> wrote:
>> Am 16.05.2018 um 13:53 schrieb Linus Walleij <linus.walleij@linaro.org>:
>> On Sat, Apr 28, 2018 at 6:31 PM, H. Nikolaus Schaller <hns@goldelico.com> wrote:
>>
>>> V5:
>>> * fix wrong split up between patches 1/7and 2/7.
>>
>> I applied patches 1, 2, 3 so we get some movement on the patch
>> set and not too much for you to rebase.
>
> thanks!
>
> Well, I already had edited the commit messages for resending...
>
>>
>> It's fine to just resend the rest next time.
>
> There is only one point open before resending:
>
> what is the preferred style to be used for PCAL_GPIO_MASK?
>
> * GENMASK(4, 0)
> * or 0x1f

No strong opinion... sorry. Whatever you & the other driver
contributors are most convenient with.

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH RFC 2/6] dt-bindings: hwmon: Add Raspberry Pi voltage sensor
From: Robin Murphy @ 2018-05-16 14:23 UTC (permalink / raw)
  To: Stefan Wahren, Rob Herring, Mark Rutland, Jean Delvare,
	Guenter Roeck, Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Phil Elwell, bcm-kernel-feedback-list, linux-rpi-kernel,
	linux-arm-kernel
In-Reply-To: <1526477827-10859-3-git-send-email-stefan.wahren@i2se.com>

On 16/05/18 14:37, Stefan Wahren wrote:
> Recent VC4 firmware provides a hardware-independent way to retrieve the
> under-voltage sensor on the following Raspberry Pi boards:
>    - Raspberry Pi B+ (via GPIO on SoC)
>    - Raspberry Pi A+ (via GPIO on SoC)
>    - Raspberry Pi 2 B (via GPIO on SoC)
>    - Raspberry Pi 3 B (via GPIO on port expander)
>    - Raspberry Pi 3 B+ (via PMIC)
> 
> TODO:
>    - try to make bcm2835 firmware a bus driver

Do you actually need a DT binding? From the probe routine in patch 2 it 
looks like this capability can be discovered dynamically by asking the 
firmware, therefore it might be more straightforward for the firmware 
driver itself to probe that and create a child device iff the feature is 
present, which the hwmon driver can then bind to as a pure platform driver.

Robin.

> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
> ---
>   .../devicetree/bindings/hwmon/raspberrypi-hwmon.txt   | 19 +++++++++++++++++++
>   1 file changed, 19 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
> 
> diff --git a/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt b/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
> new file mode 100644
> index 0000000..ec2523f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
> @@ -0,0 +1,19 @@
> +Raspberry Pi voltage sensor
> +---------------------------
> +
> +The VC4 firmware exposes a mailbox interface that allows the ARM core
> +to access the board's voltage sensor.
> +
> +Required properties :
> +- compatible : Should be "raspberrypi,bcm2835-hwmon"
> +
> +Example:
> +
> +firmware: firmware-rpi {
> +	compatible = "raspberrypi,bcm2835-firmware";
> +	mboxes = <&mailbox>;
> +
> +	voltage-sensor {
> +		compatible = "raspberrypi,bcm2835-hwmon";
> +	};
> +};
> 

^ permalink raw reply

* Re: [RESEND PATCH v5] mfd: arizona: Update DT doc to support more standard reset binding
From: Linus Walleij @ 2018-05-16 14:21 UTC (permalink / raw)
  To: Charles Keepax
  Cc: Lee Jones, Rob Herring, Mark Rutland, patches,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-kernel@vger.kernel.org
In-Reply-To: <20180509102348.20953-1-ckeepax@opensource.cirrus.com>

On Wed, May 9, 2018 at 12:23 PM, Charles Keepax
<ckeepax@opensource.cirrus.com> wrote:

> Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Acked-by: Lee Jones <lee.jones@linaro.org>

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH 1/5] dt-bindings: pinctrl: document the STMFX pinctrl bindings
From: Linus Walleij @ 2018-05-16 14:20 UTC (permalink / raw)
  To: Amelie DELAUNAY
  Cc: Rob Herring, Mark Rutland, Russell King, Alexandre TORGUE,
	Maxime Coquelin, open list:GPIO SUBSYSTEM,
	linux-kernel@vger.kernel.org,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Linux ARM, Lee Jones
In-Reply-To: <55392fe7-20d2-9447-60f9-4bd226b60195@st.com>

On Wed, May 9, 2018 at 9:56 AM, Amelie DELAUNAY <amelie.delaunay@st.com> wrote:

> Indeed, stmfx has other functions than GPIO. But, after comments done
> here: [1] and there: [2], it has been decided to move MFD parent/GPIO
> child drivers into a single PINCTRL/GPIO driver because of the following
> reasons:
> - Other stmfx functions (IDD measurement and TouchScreen controller) are
> not used on any of the boards using an stmfx and supported by Linux, so
> no way to test these functions, and no need to maintain them while they
> are not being used.
> - But, in the case a new board will use more than GPIO function on
> stmfx, the actual implementation allow to easily extract common init
> part of stmfx and put it in an MFD driver.
>
> So I could remove gpio sub-node and put its contents in stmfx node and
> keep single PINCTRL/GPIO driver for the time being.
> Please advise,

I would normally advice to use the right modeling from the start, create
the MFD driver and spawn the devices from there. It is confusing
if the layout of the driver(s) doesn't really match the layout of the
hardware.

I understand that it is a pain to write new MFD drivers to get your
things going and it would be "nice to get this working really quick
now" but in my experience it is better to do it right from the start.

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH v7 12/14] cpufreq: Add Kryo CPU scaling driver
From: Amit Kucheria @ 2018-05-16 14:13 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Ilia Lin, Michael Turquette, sboyd, Rob Herring, Mark Rutland,
	Menon, Nishanth, lgirdwood, Mark Brown, Andy Gross, David Brown,
	Catalin Marinas, Will Deacon, Rafael J. Wysocki, linux-clk, DTML,
	LKML, Linux PM list, linux-arm-msm,
	open list:ARM/QUALCOMM SUPPORT, lakml,
	Rajendra Nayak <rnay>
In-Reply-To: <20180516141132.aqjif7d5motmmnlo@vireshk-i7>

On Wed, May 16, 2018 at 5:11 PM, Viresh Kumar <viresh.kumar@linaro.org> wrote:
> On 16-05-18, 16:12, Amit Kucheria wrote:
>> > +       ret = PTR_ERR_OR_ZERO(opp_temp =
>> > +                             dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
>> > +       if (0 > ret)
>>
>> Any particular reason to prefer this over (ret < 0) that is generally
>> used? I've seen it used to avoid the == vs. = typos, but not for other
>> comparisons.
>>
>> Suggest sticking to what is commonly used i.e. ret < 0.
>>
>> > +               goto free_opp;
>> > +
>> > +       cpu_dev = get_cpu_device(GOLD_LEAD);
>>
>> Error check cpu_dev here?
>>
>> > +       ret = PTR_ERR_OR_ZERO(opp_temp =
>> > +                             dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
>> > +       if (0 > ret)
>> > +               goto free_opp;
>
> The goto here is wrong
>
>> > +
>> > +
>> > +       ret = PTR_ERR_OR_ZERO(platform_device_register_simple("cpufreq-dt",
>> > +                                                             -1, NULL, 0));
>> > +
>> > +       if (0 == ret)
>> > +               return 0;
>> > +
>> > +free_opp:
>> > +       dev_pm_opp_put_supported_hw(opp_temp);
>>
>> This is not needed because dev_pm_opp_set_supported_hw will free
>> memory in case of failure. This call in only needed in case of a
>> successful get.
>
> But this is still required for the case where platform device
> registration fails.

Agreed. The overall error path needs to be re-written with proper
check of return values.

^ permalink raw reply

* Re: [PATCH v7 12/14] cpufreq: Add Kryo CPU scaling driver
From: Viresh Kumar @ 2018-05-16 14:11 UTC (permalink / raw)
  To: Amit Kucheria
  Cc: Ilia Lin, Michael Turquette, sboyd, Rob Herring, Mark Rutland, nm,
	lgirdwood, broonie, Andy Gross, David Brown, catalin.marinas,
	will.deacon, Rafael J. Wysocki, linux-clk, devicetree, LKML,
	Linux PM list, linux-arm-msm, linux-soc, lakml, Rajendra Nayak,
	nicolas.dechesne, celster, tfinkel
In-Reply-To: <CAHLCerOYq4q2GE+m3MQAbMfxM4azPr95XiLKVtoPVG1Kv2Db=A@mail.gmail.com>

On 16-05-18, 16:12, Amit Kucheria wrote:
> > +       ret = PTR_ERR_OR_ZERO(opp_temp =
> > +                             dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
> > +       if (0 > ret)
> 
> Any particular reason to prefer this over (ret < 0) that is generally
> used? I've seen it used to avoid the == vs. = typos, but not for other
> comparisons.
> 
> Suggest sticking to what is commonly used i.e. ret < 0.
> 
> > +               goto free_opp;
> > +
> > +       cpu_dev = get_cpu_device(GOLD_LEAD);
> 
> Error check cpu_dev here?
> 
> > +       ret = PTR_ERR_OR_ZERO(opp_temp =
> > +                             dev_pm_opp_set_supported_hw(cpu_dev,&versions,1));
> > +       if (0 > ret)
> > +               goto free_opp;

The goto here is wrong

> > +
> > +
> > +       ret = PTR_ERR_OR_ZERO(platform_device_register_simple("cpufreq-dt",
> > +                                                             -1, NULL, 0));
> > +
> > +       if (0 == ret)
> > +               return 0;
> > +
> > +free_opp:
> > +       dev_pm_opp_put_supported_hw(opp_temp);
> 
> This is not needed because dev_pm_opp_set_supported_hw will free
> memory in case of failure. This call in only needed in case of a
> successful get.

But this is still required for the case where platform device
registration fails.

-- 
viresh

^ permalink raw reply

* [bug report] of: Introduce Device Tree resolve support.
From: Dan Carpenter @ 2018-05-16 14:11 UTC (permalink / raw)
  To: pantelis.antoniou; +Cc: devicetree

Hello Pantelis Antoniou,

The patch 7941b27b16e3: "of: Introduce Device Tree resolve support."
from Jul 4, 2014, leads to the following static checker warning:

	drivers/of/resolver.c:125 update_usages_of_a_phandle_reference()
	error: buffer underflow 'prop->value' 's32min-s32max'

drivers/of/resolver.c
    72  static int update_usages_of_a_phandle_reference(struct device_node *overlay,
    73                  struct property *prop_fixup, phandle phandle)
    74  {
    75          struct device_node *refnode;
    76          struct property *prop;
    77          char *value, *cur, *end, *node_path, *prop_name, *s;
    78          int offset, len;
                    ^^^^^^
    79          int err = 0;
    80  
    81          value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
    82          if (!value)
    83                  return -ENOMEM;
    84  
    85          /* prop_fixup contains a list of tuples of path:property_name:offset */
    86          end = value + prop_fixup->length;
    87          for (cur = value; cur < end; cur += len + 1) {
    88                  len = strlen(cur);
    89  
    90                  node_path = cur;
    91                  s = strchr(cur, ':');
    92                  if (!s) {
    93                          err = -EINVAL;
    94                          goto err_fail;
    95                  }
    96                  *s++ = '\0';
    97  
    98                  prop_name = s;
    99                  s = strchr(s, ':');
   100                  if (!s) {
   101                          err = -EINVAL;
   102                          goto err_fail;
   103                  }
   104                  *s++ = '\0';
   105  
   106                  err = kstrtoint(s, 10, &offset);
                                               ^^^^^^^
Smatch marks data that we get from the kstrtoint() as untrusted.

   107                  if (err)
   108                          goto err_fail;
   109  
   110                  refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path);
   111                  if (!refnode)
   112                          continue;
   113  
   114                  for_each_property_of_node(refnode, prop) {
   115                          if (!of_prop_cmp(prop->name, prop_name))
   116                                  break;
   117                  }
   118                  of_node_put(refnode);
   119  
   120                  if (!prop) {
   121                          err = -ENOENT;
   122                          goto err_fail;
   123                  }
   124  
   125                  *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
                                    ^^^^^^^^^^^^^^^^^^^^
So then it complains about this...

We probably trust this data and are fine with it writing to where ever
but it would be nice for reviewers to prevent the array
underflow/overflow warning.  I'm not even sure how big prop->value is
though so I don't know what the bounds check should be.

   126          }
   127  
   128  err_fail:
   129          kfree(value);
   130          return err;

regards,
dan carpenter

^ permalink raw reply

* Re: [PATCH v2 1/1] dts: qcom: db820c: Add gpio-line-names property
From: Linus Walleij @ 2018-05-16 14:10 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Andy Gross, David Brown, Rob Herring, Mark Rutland,
	Catalin Marinas, Bjorn Andersson, Will Deacon, Amit Kucheria,
	Nicolas Dechesne, Todor Tomov, dragonboard, linux-arm-msm,
	open list:ARM/QUALCOMM SUPPORT,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Linux ARM, linux-kernel@vger.kernel.org
In-Reply-To: <20180418134601.8502-2-manivannan.sadhasivam@linaro.org>

On Wed, Apr 18, 2018 at 3:46 PM, Manivannan Sadhasivam
<manivannan.sadhasivam@linaro.org> wrote:

> Add gpio-line-names property for Dragonboard820c based on APQ8096 SoC.
> There are 4 gpio-controllers present on this board, including the
> APQ8096 SoC, PM8994 (GPIO and MPP) and PMI8994 (GPIO).
>
> Lines names are derived from 96Boards CE Specification 1.0, Appendix
> "Expansion Connector Signal Description". Line names for PMI8994 MPP
> pins are not added due to the absence of the gpio-controller support.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH RFC 3/6] hwmon: Add support for RPi voltage sensor
From: Robin Murphy @ 2018-05-16 13:51 UTC (permalink / raw)
  To: Stefan Wahren, Rob Herring, Mark Rutland, Jean Delvare,
	Guenter Roeck, Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Phil Elwell, Noralf Trønnes, bcm-kernel-feedback-list,
	linux-rpi-kernel, linux-arm-kernel
In-Reply-To: <1526477827-10859-4-git-send-email-stefan.wahren@i2se.com>

Hi Stefan,

On 16/05/18 14:37, Stefan Wahren wrote:
> Currently there is no easy way to detect under-voltage conditions on a remote
> Raspberry Pi. This hwmon driver retrieves the state of the under-voltage sensor
> via mailbox interface. The handling based on Noralf's modifications to the
> downstream firmware driver. In case of an under-voltage condition only an entry
> is written to the kernel log.
> 
> CC: "Noralf Trønnes" <noralf@tronnes.org>
> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
> ---
>   drivers/hwmon/Kconfig             |  10 ++
>   drivers/hwmon/Makefile            |   1 +
>   drivers/hwmon/raspberrypi-hwmon.c | 207 ++++++++++++++++++++++++++++++++++++++
>   3 files changed, 218 insertions(+)
>   create mode 100644 drivers/hwmon/raspberrypi-hwmon.c
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 768aed5..7f935cf 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1298,6 +1298,16 @@ config SENSORS_PWM_FAN
>   	  This driver can also be built as a module.  If so, the module
>   	  will be called pwm-fan.
>   
> +config SENSORS_RASPBERRYPI_HWMON
> +	tristate "Raspberry Pi voltage monitor"
> +	depends on (ARCH_BCM2835 && RASPBERRYPI_FIRMWARE) || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)

Since RASPBERRYPI_FIRMWARE already implies ARCH_BCM2835 (via 
BCM2835_MBOX), this is just a very roundabout way to say:

	depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST

Robin.

> +	help
> +	  If you say yes here you get support for voltage sensor on the
> +	  Raspberry Pi.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called raspberrypi-hwmon.
> +
>   config SENSORS_SHT15
>   	tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
>   	depends on GPIOLIB || COMPILE_TEST
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index e7d52a3..a929770 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -141,6 +141,7 @@ obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
>   obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
>   obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
>   obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
> +obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
>   obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o
>   obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
>   obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o
> diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c
> new file mode 100644
> index 0000000..2003f6c
> --- /dev/null
> +++ b/drivers/hwmon/raspberrypi-hwmon.c
> @@ -0,0 +1,207 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Raspberry Pi voltage sensor driver
> + *
> + * Based on firmware/raspberrypi.c by Noralf Trønnes
> + *
> + * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
> + */
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include <soc/bcm2835/raspberrypi-firmware.h>
> +
> +#define UNDERVOLTAGE_STICKY_BIT	BIT(16)
> +
> +struct rpi_hwmon_data {
> +	struct device *hwmon_dev;
> +	struct rpi_firmware *fw;
> +	u32 last_throttled;
> +	struct delayed_work get_values_poll_work;
> +};
> +
> +static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
> +{
> +	u32 new_uv, old_uv, value;
> +	int ret;
> +
> +	/* Clear sticky bits */
> +	value = 0xffff;
> +
> +	ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
> +				    &value, sizeof(value));
> +	if (ret) {
> +		dev_err_once(data->hwmon_dev, "%s: Failed to get throttled (%d)\n",
> +			     __func__, ret);
> +		return;
> +	}
> +
> +	new_uv = value & UNDERVOLTAGE_STICKY_BIT;
> +	old_uv = data->last_throttled & UNDERVOLTAGE_STICKY_BIT;
> +	data->last_throttled = value;
> +
> +	if (new_uv == old_uv)
> +		return;
> +
> +	if (new_uv)
> +		dev_crit(data->hwmon_dev, "Under-voltage detected! (0x%08x)\n",
> +			 value);
> +	else
> +		dev_info(data->hwmon_dev, "Voltage normalised (0x%08x)\n",
> +			 value);
> +
> +	sysfs_notify(&data->hwmon_dev->kobj, NULL, "in0_lcrit_alarm");
> +}
> +
> +static void get_values_poll(struct work_struct *work)
> +{
> +	struct rpi_hwmon_data *data;
> +
> +	data = container_of(work, struct rpi_hwmon_data,
> +			    get_values_poll_work.work);
> +
> +	rpi_firmware_get_throttled(data);
> +
> +	/*
> +	 * We can't run faster than the sticky shift (100ms) since we get
> +	 * flipping in the sticky bits that are cleared.
> +	 */
> +	schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
> +}
> +
> +static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
> +		    u32 attr, int channel, long *val)
> +{
> +	struct rpi_hwmon_data *data = dev_get_drvdata(dev);
> +
> +	if (type != hwmon_in)
> +		return -EOPNOTSUPP;
> +
> +	if (attr != hwmon_in_lcrit_alarm)
> +		return -EOPNOTSUPP;
> +
> +	if (channel)
> +		return -EOPNOTSUPP;
> +
> +	*val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
> +	return 0;
> +}
> +
> +static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
> +			      u32 attr, int channel)
> +{
> +	if (type != hwmon_in)
> +		return 0;
> +
> +	if (attr != hwmon_in_lcrit_alarm)
> +		return 0;
> +
> +	if (channel)
> +		return 0;
> +
> +	return 0444;
> +}
> +
> +static const u32 rpi_in_config[] = {
> +	HWMON_I_LCRIT_ALARM,
> +	0
> +};
> +
> +static const struct hwmon_channel_info rpi_in = {
> +	.type = hwmon_in,
> +	.config = rpi_in_config,
> +};
> +
> +static const struct hwmon_channel_info *rpi_info[] = {
> +	&rpi_in,
> +	NULL
> +};
> +
> +static const struct hwmon_ops rpi_hwmon_ops = {
> +	.is_visible = rpi_is_visible,
> +	.read = rpi_read,
> +};
> +
> +static const struct hwmon_chip_info rpi_chip_info = {
> +	.ops = &rpi_hwmon_ops,
> +	.info = rpi_info,
> +};
> +
> +static int rpi_hwmon_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *fw_node;
> +	struct rpi_hwmon_data *data;
> +	int ret;
> +
> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	fw_node = of_get_parent(dev->of_node);
> +	if (!fw_node) {
> +		dev_err(dev, "Missing firmware node\n");
> +		return -ENOENT;
> +	}
> +
> +	data->fw = rpi_firmware_get(fw_node);
> +	of_node_put(fw_node);
> +	if (!data->fw)
> +		return -EPROBE_DEFER;
> +
> +	ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
> +				    &data->last_throttled,
> +				    sizeof(data->last_throttled));
> +	if (ret) {
> +		dev_info(dev, "Firmware doesn't support GET_THROTTLED\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt",
> +							       data,
> +							       &rpi_chip_info,
> +							       NULL);
> +
> +	INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll);
> +	platform_set_drvdata(pdev, data);
> +
> +	if (!PTR_ERR_OR_ZERO(data->hwmon_dev))
> +		schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
> +
> +	return PTR_ERR_OR_ZERO(data->hwmon_dev);
> +}
> +
> +static int rpi_hwmon_remove(struct platform_device *pdev)
> +{
> +	struct rpi_hwmon_data *data = platform_get_drvdata(pdev);
> +
> +	cancel_delayed_work_sync(&data->get_values_poll_work);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id rpi_hwmon_of_match[] = {
> +	{ .compatible = "raspberrypi,bcm2835-hwmon", },
> +	{ /* sentinel */},
> +};
> +MODULE_DEVICE_TABLE(of, rpi_hwmon_of_match);
> +
> +static struct platform_driver rpi_hwmon_driver = {
> +	.probe = rpi_hwmon_probe,
> +	.remove = rpi_hwmon_remove,
> +	.driver = {
> +		.name = "raspberrypi-hwmon",
> +		.of_match_table = rpi_hwmon_of_match,
> +	},
> +};
> +module_platform_driver(rpi_hwmon_driver);
> +
> +MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
> +MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
> +MODULE_LICENSE("GPL v2");
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH v3 0/2] add support for HiSilicon STB xHCI host controller
From: Mathias Nyman @ 2018-05-16 13:48 UTC (permalink / raw)
  To: sunjg79, mathias.nyman, gregkh, robh+dt
  Cc: shawn.guo, xuejiancheng, chunfeng.yun, linux-usb, devicetree,
	Jianguo Sun
In-Reply-To: <1526278842-13883-1-git-send-email-sunjg79@163.com>

On 14.05.2018 09:20, sunjg79@163.com wrote:
> From: Jianguo Sun <sunjianguo1@huawei.com>
> 
> This patch set adds bindings doc and xhci driver for xHCI host controller
> on HiSilicon STB SoCs.
> 

Looks good to me, adding to queue

-Mathias

^ permalink raw reply

* [PATCH v2 3/3] arm64: dts: renesas: draak: Describe HDMI input
From: Jacopo Mondi @ 2018-05-16 13:42 UTC (permalink / raw)
  To: niklas.soderlund, laurent.pinchart, horms, geert, magnus.damm,
	robh+dt
  Cc: Jacopo Mondi, linux-renesas-soc, devicetree, linux-arm-kernel,
	linux-kernel
In-Reply-To: <1526478129-16465-1-git-send-email-jacopo+renesas@jmondi.org>

Describe HDMI input connector and ADV7612 HDMI decoder installed on
R-Car Gen3 Draak board.

The video signal routing to the HDMI decoder to the video input interface
VIN4 is multiplexed with CVBS input path, and enabled/disabled through
on-board switches SW-49, SW-50, SW-51 and SW-52.

As the default board switches configuration connects CVBS input to VIN4,
leave the HDMI decoder unconnected in DTS.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
 arch/arm64/boot/dts/renesas/r8a77995-draak.dts | 38 ++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
index 9aba28f..ea99dc9 100644
--- a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
+++ b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
@@ -59,6 +59,17 @@
 		};
 	};
 
+	hdmi-in {
+		compatible = "hdmi-connector";
+		type = "a";
+
+		port {
+			hdmi_con_in: endpoint {
+				remote-endpoint = <&adv7612_in>;
+			};
+		};
+	};
+
 	memory@48000000 {
 		device_type = "memory";
 		/* first 128MB is reserved for secure area. */
@@ -170,6 +181,33 @@
 			};
 		};
 	};
+
+	hdmi-decoder@4c {
+		compatible = "adi,adv7612";
+		reg = <0x4c>;
+		default-input = <0>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				adv7612_in: endpoint {
+					remote-endpoint = <&hdmi_con_in>;
+				};
+			};
+
+			port@2 {
+				reg = <2>;
+				adv7612_out: endpoint {
+					pclk-sample = <0>;
+					hsync-active = <0>;
+					vsync-active = <0>;
+				};
+			};
+		};
+	};
 };
 
 &i2c1 {
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 2/3] arm64: dts: renesas: draak: Describe CVBS input
From: Jacopo Mondi @ 2018-05-16 13:42 UTC (permalink / raw)
  To: niklas.soderlund, laurent.pinchart, horms, geert, magnus.damm,
	robh+dt
  Cc: Jacopo Mondi, linux-renesas-soc, devicetree, linux-arm-kernel,
	linux-kernel
In-Reply-To: <1526478129-16465-1-git-send-email-jacopo+renesas@jmondi.org>

Describe CVBS video input through analog video decoder ADV7180
connected to video input interface VIN4.

The video input signal path is shared with HDMI video input, and
selected by on-board switches SW-53 and SW-54 with CVBS input selected
by the default switches configuration.
---
 arch/arm64/boot/dts/renesas/r8a77995-draak.dts | 36 ++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
index d03f194..9aba28f 100644
--- a/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
+++ b/arch/arm64/boot/dts/renesas/r8a77995-draak.dts
@@ -142,6 +142,11 @@
 		groups = "usb0";
 		function = "usb0";
 	};
+
+	vin4_pins_cvbs: vin4 {
+		groups = "vin4_data8", "vin4_sync", "vin4_clk";
+		function = "vin4";
+	};
 };
 
 &i2c0 {
@@ -154,6 +159,17 @@
 		reg = <0x50>;
 		pagesize = <8>;
 	};
+
+	analog-video@20 {
+		compatible = "adi,adv7180";
+		reg = <0x20>;
+
+		port {
+			adv7180_out: endpoint {
+				remote-endpoint = <&vin4_in>;
+			};
+		};
+	};
 };
 
 &i2c1 {
@@ -246,3 +262,23 @@
 	timeout-sec = <60>;
 	status = "okay";
 };
+
+&vin4 {
+	pinctrl-0 = <&vin4_pins_cvbs>;
+	pinctrl-names = "default";
+
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+
+			vin4_in: endpoint {
+				remote-endpoint = <&adv7180_out>;
+			};
+		};
+	};
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 1/3] dt-bindings: media: rcar-vin: Add R8A77995 support
From: Jacopo Mondi @ 2018-05-16 13:42 UTC (permalink / raw)
  To: niklas.soderlund, laurent.pinchart, horms, geert, magnus.damm,
	robh+dt
  Cc: Jacopo Mondi, linux-renesas-soc, devicetree, linux-arm-kernel,
	linux-kernel
In-Reply-To: <1526478129-16465-1-git-send-email-jacopo+renesas@jmondi.org>

Add compatible string for R-Car D3 R8A7795 to list of SoCs supported by
rcar-vin driver.

Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Acked-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Simon Horman <horms+renesas@verge.net.au>
---
 Documentation/devicetree/bindings/media/rcar_vin.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt
index a19517e1..5c6f2a7 100644
--- a/Documentation/devicetree/bindings/media/rcar_vin.txt
+++ b/Documentation/devicetree/bindings/media/rcar_vin.txt
@@ -22,6 +22,7 @@ on Gen3 platforms to a CSI-2 receiver.
    - "renesas,vin-r8a7795" for the R8A7795 device
    - "renesas,vin-r8a7796" for the R8A7796 device
    - "renesas,vin-r8a77970" for the R8A77970 device
+   - "renesas,vin-r8a77995" for the R8A77995 device
    - "renesas,rcar-gen2-vin" for a generic R-Car Gen2 or RZ/G1 compatible
      device.
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v2 0/3] arm64: dts: Draak: Enable video inputs and VIN4
From: Jacopo Mondi @ 2018-05-16 13:42 UTC (permalink / raw)
  To: niklas.soderlund, laurent.pinchart, horms, geert, magnus.damm,
	robh+dt
  Cc: Jacopo Mondi, linux-renesas-soc, devicetree, linux-arm-kernel,
	linux-kernel

Hello,
   the second iteration of this series enables HDMI and CVBS video inputs
for the Draak board.

>From v1 series the description of VIN4 video interface for R8A77995 SoC has
already been collected by Simon, thus I'm not resending it.

This new version describes the HDMI input as v1 did, but does not connect it
to the VIN4 interface, as the video input path is shared between HDMI and
CVBS, and the default switches configuration selects CVBS.

I've not been able to test CVBS capture as I don't have any CVBS video source
here, but the devices shows up in the media controller graph as expected.

-------------------------------------------------------------------------------
[root@alarm ~]# media-ctl  -p -d /dev/media1
Media controller API version 4.17.0

Media device information
------------------------
driver          rcar_vin
model           renesas,vin-r8a77995
serial
bus info        platform:e6ef4000.video
hw revision     0x0
driver version  4.17.0

Device topology
- entity 1: adv7180 0-0020 (1 pad, 1 link)
            type V4L2 subdev subtype Unknown flags 20004
            device node name /dev/v4l-subdev6
        pad0: Source
                [fmt:UYVY8_2X8/720x480 field:interlaced colorspace:smpte170m]
                -> "VIN4 output":0 []

- entity 5: VIN4 output (1 pad, 1 link)
            type Node subtype V4L flags 0
            device node name /dev/video3
        pad0: Sink
                <- "adv7180 0-0020":0 []
-------------------------------------------------------------------------------

Compared to Gen2 boards that use adv7180 (Lager, Alt, Koelsch, Porter, Gose
and Silk) I have removed the 'bus-width' property as it is not parsed by the
driver nor mentioned in the chip device tree binding description.

Switching from CVBS to HDMI input requires connecting the HDMI decoder endpoint
to VIN4, change the pin muxing to increase the VIN4 input bus width to 24 bits,
and add the synchronism signal polarities to the vin endpoint, as the HDMI
decoder uses BT.601 while the analog video decoder uses BT.656 unconditionally.

The series has been developed on top of media-master tree but applies cleanly
on top of latest renesas-driver.

Patches for testing are available at:
git://jmondi.org/linux d3/media-master/driver-v2
git://jmondi.org/linux d3/media-master/dts-v2
git://jmondi.org/vin-tests d3

Thanks
    j

Jacopo Mondi (3):
  dt-bindings: media: rcar-vin: Add R8A77995 support
  arm64: dts: renesas: draak: Describe CVBS input
  arm64: dts: renesas: draak: Describe HDMI input

 .../devicetree/bindings/media/rcar_vin.txt         |  1 +
 arch/arm64/boot/dts/renesas/r8a77995-draak.dts     | 74 ++++++++++++++++++++++
 2 files changed, 75 insertions(+)

--
2.7.4

^ permalink raw reply

* [PATCH RFC 6/6] arm64: defconfig: Enable RPi voltage sensor
From: Stefan Wahren @ 2018-05-16 13:37 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jean Delvare, Guenter Roeck,
	Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Stefan Wahren, Phil Elwell, bcm-kernel-feedback-list,
	linux-rpi-kernel, linux-arm-kernel
In-Reply-To: <1526477827-10859-1-git-send-email-stefan.wahren@i2se.com>

The patch enables the hwmon driver for the Raspberry Pi.

Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
 arch/arm64/configs/defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index d25121b..5cdecef 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -352,6 +352,7 @@ CONFIG_BATTERY_BQ27XXX=y
 CONFIG_SENSORS_ARM_SCPI=y
 CONFIG_SENSORS_LM90=m
 CONFIG_SENSORS_INA2XX=m
+CONFIG_SENSORS_RASPBERRYPI_HWMON=m
 CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y
 CONFIG_CPU_THERMAL=y
 CONFIG_THERMAL_EMULATION=y
-- 
2.7.4

^ permalink raw reply related

* [PATCH RFC 5/6] ARM: multi_v7_defconfig: Enable RPi voltage sensor
From: Stefan Wahren @ 2018-05-16 13:37 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jean Delvare, Guenter Roeck,
	Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Stefan Wahren, Phil Elwell, bcm-kernel-feedback-list,
	linux-rpi-kernel, linux-arm-kernel
In-Reply-To: <1526477827-10859-1-git-send-email-stefan.wahren@i2se.com>

The patch enables the hwmon driver for the Raspberry Pi.

Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 720461b..5c9dc00 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -477,6 +477,7 @@ CONFIG_SENSORS_LM90=y
 CONFIG_SENSORS_LM95245=y
 CONFIG_SENSORS_NTC_THERMISTOR=m
 CONFIG_SENSORS_PWM_FAN=m
+CONFIG_SENSORS_RASPBERRYPI_HWMON=m
 CONFIG_SENSORS_INA2XX=m
 CONFIG_CPU_THERMAL=y
 CONFIG_BCM2835_THERMAL=m
-- 
2.7.4

^ permalink raw reply related

* [PATCH RFC 4/6] ARM: bcm2835_defconfig: Enable RPi voltage sensor
From: Stefan Wahren @ 2018-05-16 13:37 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jean Delvare, Guenter Roeck,
	Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Stefan Wahren, Phil Elwell, bcm-kernel-feedback-list,
	linux-rpi-kernel, linux-arm-kernel
In-Reply-To: <1526477827-10859-1-git-send-email-stefan.wahren@i2se.com>

The patch enables the hwmon driver for the Raspberry Pi.

Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
 arch/arm/configs/bcm2835_defconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig
index e4d188f..e9bc889 100644
--- a/arch/arm/configs/bcm2835_defconfig
+++ b/arch/arm/configs/bcm2835_defconfig
@@ -86,7 +86,7 @@ CONFIG_SPI=y
 CONFIG_SPI_BCM2835=y
 CONFIG_SPI_BCM2835AUX=y
 CONFIG_GPIO_SYSFS=y
-# CONFIG_HWMON is not set
+CONFIG_SENSORS_RASPBERRYPI_HWMON=m
 CONFIG_THERMAL=y
 CONFIG_BCM2835_THERMAL=y
 CONFIG_WATCHDOG=y
-- 
2.7.4

^ permalink raw reply related

* [PATCH RFC 3/6] hwmon: Add support for RPi voltage sensor
From: Stefan Wahren @ 2018-05-16 13:37 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jean Delvare, Guenter Roeck,
	Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Stefan Wahren, Phil Elwell, Noralf Trønnes,
	bcm-kernel-feedback-list, linux-rpi-kernel, linux-arm-kernel
In-Reply-To: <1526477827-10859-1-git-send-email-stefan.wahren@i2se.com>

Currently there is no easy way to detect under-voltage conditions on a remote
Raspberry Pi. This hwmon driver retrieves the state of the under-voltage sensor
via mailbox interface. The handling based on Noralf's modifications to the
downstream firmware driver. In case of an under-voltage condition only an entry
is written to the kernel log.

CC: "Noralf Trønnes" <noralf@tronnes.org>
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
 drivers/hwmon/Kconfig             |  10 ++
 drivers/hwmon/Makefile            |   1 +
 drivers/hwmon/raspberrypi-hwmon.c | 207 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 218 insertions(+)
 create mode 100644 drivers/hwmon/raspberrypi-hwmon.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 768aed5..7f935cf 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1298,6 +1298,16 @@ config SENSORS_PWM_FAN
 	  This driver can also be built as a module.  If so, the module
 	  will be called pwm-fan.
 
+config SENSORS_RASPBERRYPI_HWMON
+	tristate "Raspberry Pi voltage monitor"
+	depends on (ARCH_BCM2835 && RASPBERRYPI_FIRMWARE) || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
+	help
+	  If you say yes here you get support for voltage sensor on the
+	  Raspberry Pi.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called raspberrypi-hwmon.
+
 config SENSORS_SHT15
 	tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
 	depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index e7d52a3..a929770 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
+obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
 obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o
 obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
 obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o
diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c
new file mode 100644
index 0000000..2003f6c
--- /dev/null
+++ b/drivers/hwmon/raspberrypi-hwmon.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Raspberry Pi voltage sensor driver
+ *
+ * Based on firmware/raspberrypi.c by Noralf Trønnes
+ *
+ * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define UNDERVOLTAGE_STICKY_BIT	BIT(16)
+
+struct rpi_hwmon_data {
+	struct device *hwmon_dev;
+	struct rpi_firmware *fw;
+	u32 last_throttled;
+	struct delayed_work get_values_poll_work;
+};
+
+static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
+{
+	u32 new_uv, old_uv, value;
+	int ret;
+
+	/* Clear sticky bits */
+	value = 0xffff;
+
+	ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
+				    &value, sizeof(value));
+	if (ret) {
+		dev_err_once(data->hwmon_dev, "%s: Failed to get throttled (%d)\n",
+			     __func__, ret);
+		return;
+	}
+
+	new_uv = value & UNDERVOLTAGE_STICKY_BIT;
+	old_uv = data->last_throttled & UNDERVOLTAGE_STICKY_BIT;
+	data->last_throttled = value;
+
+	if (new_uv == old_uv)
+		return;
+
+	if (new_uv)
+		dev_crit(data->hwmon_dev, "Under-voltage detected! (0x%08x)\n",
+			 value);
+	else
+		dev_info(data->hwmon_dev, "Voltage normalised (0x%08x)\n",
+			 value);
+
+	sysfs_notify(&data->hwmon_dev->kobj, NULL, "in0_lcrit_alarm");
+}
+
+static void get_values_poll(struct work_struct *work)
+{
+	struct rpi_hwmon_data *data;
+
+	data = container_of(work, struct rpi_hwmon_data,
+			    get_values_poll_work.work);
+
+	rpi_firmware_get_throttled(data);
+
+	/*
+	 * We can't run faster than the sticky shift (100ms) since we get
+	 * flipping in the sticky bits that are cleared.
+	 */
+	schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
+}
+
+static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
+		    u32 attr, int channel, long *val)
+{
+	struct rpi_hwmon_data *data = dev_get_drvdata(dev);
+
+	if (type != hwmon_in)
+		return -EOPNOTSUPP;
+
+	if (attr != hwmon_in_lcrit_alarm)
+		return -EOPNOTSUPP;
+
+	if (channel)
+		return -EOPNOTSUPP;
+
+	*val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
+	return 0;
+}
+
+static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
+			      u32 attr, int channel)
+{
+	if (type != hwmon_in)
+		return 0;
+
+	if (attr != hwmon_in_lcrit_alarm)
+		return 0;
+
+	if (channel)
+		return 0;
+
+	return 0444;
+}
+
+static const u32 rpi_in_config[] = {
+	HWMON_I_LCRIT_ALARM,
+	0
+};
+
+static const struct hwmon_channel_info rpi_in = {
+	.type = hwmon_in,
+	.config = rpi_in_config,
+};
+
+static const struct hwmon_channel_info *rpi_info[] = {
+	&rpi_in,
+	NULL
+};
+
+static const struct hwmon_ops rpi_hwmon_ops = {
+	.is_visible = rpi_is_visible,
+	.read = rpi_read,
+};
+
+static const struct hwmon_chip_info rpi_chip_info = {
+	.ops = &rpi_hwmon_ops,
+	.info = rpi_info,
+};
+
+static int rpi_hwmon_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *fw_node;
+	struct rpi_hwmon_data *data;
+	int ret;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	fw_node = of_get_parent(dev->of_node);
+	if (!fw_node) {
+		dev_err(dev, "Missing firmware node\n");
+		return -ENOENT;
+	}
+
+	data->fw = rpi_firmware_get(fw_node);
+	of_node_put(fw_node);
+	if (!data->fw)
+		return -EPROBE_DEFER;
+
+	ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED,
+				    &data->last_throttled,
+				    sizeof(data->last_throttled));
+	if (ret) {
+		dev_info(dev, "Firmware doesn't support GET_THROTTLED\n");
+		return -EOPNOTSUPP;
+	}
+
+	data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt",
+							       data,
+							       &rpi_chip_info,
+							       NULL);
+
+	INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll);
+	platform_set_drvdata(pdev, data);
+
+	if (!PTR_ERR_OR_ZERO(data->hwmon_dev))
+		schedule_delayed_work(&data->get_values_poll_work, 2 * HZ);
+
+	return PTR_ERR_OR_ZERO(data->hwmon_dev);
+}
+
+static int rpi_hwmon_remove(struct platform_device *pdev)
+{
+	struct rpi_hwmon_data *data = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&data->get_values_poll_work);
+
+	return 0;
+}
+
+static const struct of_device_id rpi_hwmon_of_match[] = {
+	{ .compatible = "raspberrypi,bcm2835-hwmon", },
+	{ /* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, rpi_hwmon_of_match);
+
+static struct platform_driver rpi_hwmon_driver = {
+	.probe = rpi_hwmon_probe,
+	.remove = rpi_hwmon_remove,
+	.driver = {
+		.name = "raspberrypi-hwmon",
+		.of_match_table = rpi_hwmon_of_match,
+	},
+};
+module_platform_driver(rpi_hwmon_driver);
+
+MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
+MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH RFC 2/6] dt-bindings: hwmon: Add Raspberry Pi voltage sensor
From: Stefan Wahren @ 2018-05-16 13:37 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jean Delvare, Guenter Roeck,
	Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Stefan Wahren, Phil Elwell, bcm-kernel-feedback-list,
	linux-rpi-kernel, linux-arm-kernel
In-Reply-To: <1526477827-10859-1-git-send-email-stefan.wahren@i2se.com>

Recent VC4 firmware provides a hardware-independent way to retrieve the
under-voltage sensor on the following Raspberry Pi boards:
  - Raspberry Pi B+ (via GPIO on SoC)
  - Raspberry Pi A+ (via GPIO on SoC)
  - Raspberry Pi 2 B (via GPIO on SoC)
  - Raspberry Pi 3 B (via GPIO on port expander)
  - Raspberry Pi 3 B+ (via PMIC)

TODO:
  - try to make bcm2835 firmware a bus driver

Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
 .../devicetree/bindings/hwmon/raspberrypi-hwmon.txt   | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt

diff --git a/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt b/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
new file mode 100644
index 0000000..ec2523f
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
@@ -0,0 +1,19 @@
+Raspberry Pi voltage sensor
+---------------------------
+
+The VC4 firmware exposes a mailbox interface that allows the ARM core
+to access the board's voltage sensor.
+
+Required properties :
+- compatible : Should be "raspberrypi,bcm2835-hwmon"
+
+Example:
+
+firmware: firmware-rpi {
+	compatible = "raspberrypi,bcm2835-firmware";
+	mboxes = <&mailbox>;
+
+	voltage-sensor {
+		compatible = "raspberrypi,bcm2835-hwmon";
+	};
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH RFC 1/6] ARM: bcm2835: Add GET_THROTTLED firmware property
From: Stefan Wahren @ 2018-05-16 13:37 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jean Delvare, Guenter Roeck,
	Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Stefan Wahren, Phil Elwell, bcm-kernel-feedback-list,
	linux-rpi-kernel, linux-arm-kernel
In-Reply-To: <1526477827-10859-1-git-send-email-stefan.wahren@i2se.com>

Recent Raspberry Pi firmware provides a mailbox property to detect
under-voltage conditions. Here is the current definition.

The u32 value returned by the firmware is divided into 2 parts:
  - lower 16-bits are the live value
  - upper 16-bits are the history or sticky value

  Bits:
  0: under-voltage
  1: arm frequency capped
  2: currently throttled 
  16: under-voltage has occurred
  17: arm frequency capped has occurred
  18: throttling has occurred

Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
 include/soc/bcm2835/raspberrypi-firmware.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h
index 8ee8991..c4a5c9e 100644
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -75,6 +75,7 @@ enum rpi_firmware_property_tag {
 	RPI_FIRMWARE_GET_EDID_BLOCK =                         0x00030020,
 	RPI_FIRMWARE_GET_CUSTOMER_OTP =                       0x00030021,
 	RPI_FIRMWARE_GET_DOMAIN_STATE =                       0x00030030,
+	RPI_FIRMWARE_GET_THROTTLED =                          0x00030046,
 	RPI_FIRMWARE_SET_CLOCK_STATE =                        0x00038001,
 	RPI_FIRMWARE_SET_CLOCK_RATE =                         0x00038002,
 	RPI_FIRMWARE_SET_VOLTAGE =                            0x00038003,
-- 
2.7.4

^ permalink raw reply related

* [PATCH RFC 0/6] hwmon: Add support for Raspberry Pi voltage sensor
From: Stefan Wahren @ 2018-05-16 13:37 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jean Delvare, Guenter Roeck,
	Eric Anholt
  Cc: linux-hwmon, devicetree, Florian Fainelli, Scott Branden, Ray Jui,
	Stefan Wahren, Phil Elwell, bcm-kernel-feedback-list,
	linux-rpi-kernel, linux-arm-kernel

A common issue for the Raspberry Pi is an inadequate power supply. 
Noralf Trønnes started a discussion [1] about writting such under-voltage
conditions into the kernel log.

This series is a draft to upstream the resulting kernel patch and is not
intended for 4.18.

[1] - https://github.com/raspberrypi/linux/issues/2367

Stefan Wahren (6):
  ARM: bcm2835: Add GET_THROTTLED firmware property
  dt-bindings: hwmon: Add Raspberry Pi voltage sensor
  hwmon: Add support for RPi voltage sensor
  ARM: bcm2835_defconfig: Enable RPi voltage sensor
  ARM: multi_v7_defconfig: Enable RPi voltage sensor
  arm64: defconfig: Enable RPi voltage sensor

 .../bindings/hwmon/raspberrypi-hwmon.txt           |  19 ++
 arch/arm/configs/bcm2835_defconfig                 |   2 +-
 arch/arm/configs/multi_v7_defconfig                |   1 +
 arch/arm64/configs/defconfig                       |   1 +
 drivers/hwmon/Kconfig                              |  10 +
 drivers/hwmon/Makefile                             |   1 +
 drivers/hwmon/raspberrypi-hwmon.c                  | 207 +++++++++++++++++++++
 include/soc/bcm2835/raspberrypi-firmware.h         |   1 +
 8 files changed, 241 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/hwmon/raspberrypi-hwmon.txt
 create mode 100644 drivers/hwmon/raspberrypi-hwmon.c

-- 
2.7.4


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox