1849 lines
55 KiB
Diff
1849 lines
55 KiB
Diff
From 1d34419db80914e8fcd39e622a56c3fdf0b2fe30 Mon Sep 17 00:00:00 2001
|
|
From: Lionel VITTE <lionel.vitte@st.com>
|
|
Date: Mon, 5 Oct 2020 13:19:46 +0200
|
|
Subject: [PATCH 12/22] ARM-stm32mp1-r2-rc8-MEDIA-SOC-THERMAL
|
|
|
|
---
|
|
.../bindings/media/video-interfaces.txt | 2 +
|
|
.../bindings/soc/stm32/stm32_hdp.txt | 39 ++
|
|
drivers/media/i2c/ov5640.c | 77 +++-
|
|
drivers/media/platform/stm32/stm32-cec.c | 10 +-
|
|
drivers/media/platform/stm32/stm32-dcmi.c | 100 +++--
|
|
drivers/media/v4l2-core/v4l2-fwnode.c | 3 +
|
|
drivers/soc/Kconfig | 1 +
|
|
drivers/soc/Makefile | 1 +
|
|
drivers/soc/st/Kconfig | 17 +
|
|
drivers/soc/st/Makefile | 2 +
|
|
drivers/soc/st/stm32_hdp.c | 242 +++++++++++
|
|
drivers/soc/st/stm32_pm_domain.c | 212 ++++++++++
|
|
drivers/thermal/st/stm_thermal.c | 383 ++++++------------
|
|
include/dt-bindings/soc/stm32-hdp.h | 108 +++++
|
|
include/media/v4l2-fwnode.h | 2 +
|
|
15 files changed, 882 insertions(+), 317 deletions(-)
|
|
create mode 100644 Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt
|
|
create mode 100644 drivers/soc/st/Kconfig
|
|
create mode 100644 drivers/soc/st/Makefile
|
|
create mode 100644 drivers/soc/st/stm32_hdp.c
|
|
create mode 100644 drivers/soc/st/stm32_pm_domain.c
|
|
create mode 100644 include/dt-bindings/soc/stm32-hdp.h
|
|
|
|
diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt
|
|
index f884ada0bffc8..aff685a25aca9 100644
|
|
--- a/Documentation/devicetree/bindings/media/video-interfaces.txt
|
|
+++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
|
|
@@ -149,6 +149,8 @@ Optional endpoint properties
|
|
as 0 (normal). This property is valid for serial busses only.
|
|
- strobe: Whether the clock signal is used as clock (0) or strobe (1). Used
|
|
with CCP2, for instance.
|
|
+- pclk-max-frequency: maximum pixel clock frequency admissible by video
|
|
+ host interface.
|
|
|
|
Example
|
|
-------
|
|
diff --git a/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt b/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt
|
|
new file mode 100644
|
|
index 0000000000000..e2bd82f4980eb
|
|
--- /dev/null
|
|
+++ b/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt
|
|
@@ -0,0 +1,39 @@
|
|
+STM32 - STM32MP1- HDP Pin configuration for STM32MP1
|
|
+=======================================================
|
|
+
|
|
+The Hardware Debug Port (HDP) allows the observation of internal signals. By using multiplexers,
|
|
+up to 16 signals for each of 8-bit output can be observed.
|
|
+
|
|
+Required Properties:
|
|
+
|
|
+ - compatible: Must be "st,stm32mp1-hdp"
|
|
+ - muxing-hdp: Indicates for each HDP pins selected which HDP output among the 16 available signals you want
|
|
+
|
|
+For each HDP pins you can select one of 16 signals which will be described in file : include/dt-bindings/soc/stm32-hdp.h
|
|
+
|
|
+Example
|
|
+-------
|
|
+
|
|
+In common dtsi file:
|
|
+
|
|
+hdp: hdp@5002a000 {
|
|
+ compatible = "st,stm32mp1-hdp";
|
|
+ reg = <0x5002a000 0x400>;
|
|
+ clocks = <&rcc HDP>;
|
|
+ clock-names = "hdp";
|
|
+};
|
|
+
|
|
+In board-specific file:
|
|
+
|
|
+In this example I've selected HDP0, HDP6 and HDP7, and for HDP0 the output signal is HDP0_GPOVAL_0,
|
|
+for HDP6 is HDP6_GPOVAL_6, and for HDP7 is HDP7_GPOVAL_7.
|
|
+
|
|
+&hdp {
|
|
+ pinctrl-names = "default", "sleep";
|
|
+ pinctrl-0 = <&hdp0_pins_a &hdp6_pins_a &hdp7_pins_a>;
|
|
+ pinctrl-1 = <&hdp0_pins_sleep_a &hdp6_pins_sleep_a &hdp7_pins_sleep_a>;
|
|
+
|
|
+ muxing-hdp = <(STM32_HDP(0, HDP0_GPOVAL_0) |
|
|
+ STM32_HDP(6, HDP6_GPOVAL_6) |
|
|
+ STM32_HDP(7, HDP7_GPOVAL_7))>;
|
|
+};
|
|
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
|
|
index 266e947572c1e..6a1b50bec7c84 100644
|
|
--- a/drivers/media/i2c/ov5640.c
|
|
+++ b/drivers/media/i2c/ov5640.c
|
|
@@ -63,6 +63,7 @@
|
|
#define OV5640_REG_TIMING_VTS 0x380e
|
|
#define OV5640_REG_TIMING_TC_REG20 0x3820
|
|
#define OV5640_REG_TIMING_TC_REG21 0x3821
|
|
+#define OV5640_REG_DVP_PCLK_DIVIDER 0x3824
|
|
#define OV5640_REG_AEC_CTRL00 0x3a00
|
|
#define OV5640_REG_AEC_B50_STEP 0x3a08
|
|
#define OV5640_REG_AEC_B60_STEP 0x3a0a
|
|
@@ -214,6 +215,7 @@ struct ov5640_ctrls {
|
|
struct v4l2_ctrl *test_pattern;
|
|
struct v4l2_ctrl *hflip;
|
|
struct v4l2_ctrl *vflip;
|
|
+ struct v4l2_ctrl *link_freq;
|
|
};
|
|
|
|
struct ov5640_dev {
|
|
@@ -370,8 +372,8 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0},
|
|
+ {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_XGA_1024_768[] = {
|
|
@@ -389,8 +391,7 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_QVGA_320_240[] = {
|
|
@@ -408,8 +409,7 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_QCIF_176_144[] = {
|
|
@@ -427,8 +427,7 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_NTSC_720_480[] = {
|
|
@@ -446,8 +445,7 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_PAL_720_576[] = {
|
|
@@ -465,8 +463,7 @@ static const struct reg_value ov5640_setting_PAL_720_576[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_720P_1280_720[] = {
|
|
@@ -484,8 +481,7 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = {
|
|
{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
|
|
{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
|
|
- {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0},
|
|
};
|
|
|
|
static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
|
@@ -504,8 +500,8 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0},
|
|
+ {0x4407, 0x04, 0, 0},
|
|
+ {0x5001, 0x83, 0, 0},
|
|
{0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
|
|
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
|
{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
|
|
@@ -516,7 +512,6 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
|
{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
|
|
{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
|
|
{0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0},
|
|
- {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
|
|
{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
|
|
};
|
|
|
|
@@ -535,8 +530,8 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
|
|
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
|
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
|
{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
|
|
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
|
- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
|
|
+ {0x4407, 0x04, 0, 0},
|
|
+ {0x5001, 0x83, 0, 70},
|
|
};
|
|
|
|
/* power-on sensor init reg table */
|
|
@@ -1005,9 +1000,38 @@ static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor,
|
|
|
|
static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
|
|
{
|
|
+ const struct ov5640_mode_info *mode = sensor->current_mode;
|
|
u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
|
|
+ struct i2c_client *client = sensor->i2c_client;
|
|
+ unsigned int pclk_freq, max_pclk_freq;
|
|
+ u8 dvp_pclk_divider;
|
|
int ret;
|
|
|
|
+ /*
|
|
+ * 1280x720 and 1024x768 are reported to use 'SUBSAMPLING' only,
|
|
+ * but they seems to go through the scaler before subsampling.
|
|
+ */
|
|
+ if (mode->dn_mode == SCALING ||
|
|
+ (mode->id == OV5640_MODE_720P_1280_720) ||
|
|
+ (mode->id == OV5640_MODE_XGA_1024_768))
|
|
+ dvp_pclk_divider = 1;
|
|
+ else
|
|
+ dvp_pclk_divider = 2;
|
|
+
|
|
+ ret = ov5640_write_reg(sensor, OV5640_REG_DVP_PCLK_DIVIDER,
|
|
+ dvp_pclk_divider);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ pclk_freq = rate / dvp_pclk_divider;
|
|
+ max_pclk_freq = sensor->ep.bus.parallel.pclk_max_frequency;
|
|
+
|
|
+ /* clip rate according to optional maximum pixel clock limit */
|
|
+ if (max_pclk_freq && (pclk_freq > max_pclk_freq)) {
|
|
+ rate = max_pclk_freq * dvp_pclk_divider;
|
|
+ dev_dbg(&client->dev, "DVP pixel clock too high (%d > %d Hz), reducing rate...\n",
|
|
+ pclk_freq, max_pclk_freq);
|
|
+ }
|
|
+
|
|
ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv,
|
|
&bit_div, &pclk_div);
|
|
|
|
@@ -1042,6 +1066,7 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
|
|
(ilog2(pclk_div) << 4));
|
|
}
|
|
|
|
+#if 0
|
|
/* set JPEG framing sizes */
|
|
static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
|
|
const struct ov5640_mode_info *mode)
|
|
@@ -1065,19 +1090,20 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor,
|
|
|
|
return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact);
|
|
}
|
|
+#endif
|
|
|
|
/* download ov5640 settings to sensor through i2c */
|
|
static int ov5640_set_timings(struct ov5640_dev *sensor,
|
|
const struct ov5640_mode_info *mode)
|
|
{
|
|
int ret;
|
|
-
|
|
+#if 0
|
|
if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) {
|
|
ret = ov5640_set_jpeg_timings(sensor, mode);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
-
|
|
+#endif
|
|
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
|
|
if (ret < 0)
|
|
return ret;
|
|
@@ -2199,6 +2225,10 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
|
|
return 0;
|
|
}
|
|
|
|
+static const s64 link_freq_menu_items[] = {
|
|
+ 384000000,
|
|
+};
|
|
+
|
|
static int ov5640_set_fmt(struct v4l2_subdev *sd,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_format *format)
|
|
@@ -2637,6 +2667,8 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
case V4L2_CID_VFLIP:
|
|
ret = ov5640_set_ctrl_vflip(sensor, ctrl->val);
|
|
break;
|
|
+ case V4L2_CID_LINK_FREQ:
|
|
+ return 0;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
@@ -2704,6 +2736,9 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
|
|
V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
|
|
V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
|
|
|
|
+ ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
|
|
+ 0, 0, link_freq_menu_items);
|
|
+
|
|
if (hdl->error) {
|
|
ret = hdl->error;
|
|
goto free_ctrls;
|
|
diff --git a/drivers/media/platform/stm32/stm32-cec.c b/drivers/media/platform/stm32/stm32-cec.c
|
|
index 8a86b2cc22fab..108f83c264f84 100644
|
|
--- a/drivers/media/platform/stm32/stm32-cec.c
|
|
+++ b/drivers/media/platform/stm32/stm32-cec.c
|
|
@@ -291,7 +291,9 @@ static int stm32_cec_probe(struct platform_device *pdev)
|
|
|
|
cec->clk_cec = devm_clk_get(&pdev->dev, "cec");
|
|
if (IS_ERR(cec->clk_cec)) {
|
|
- dev_err(&pdev->dev, "Cannot get cec clock\n");
|
|
+ if (PTR_ERR(cec->clk_cec) != -EPROBE_DEFER)
|
|
+ dev_err(&pdev->dev, "Cannot get cec clock\n");
|
|
+
|
|
return PTR_ERR(cec->clk_cec);
|
|
}
|
|
|
|
@@ -302,10 +304,14 @@ static int stm32_cec_probe(struct platform_device *pdev)
|
|
}
|
|
|
|
cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec");
|
|
+ if (IS_ERR(cec->clk_hdmi_cec) &&
|
|
+ PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER)
|
|
+ return -EPROBE_DEFER;
|
|
+
|
|
if (!IS_ERR(cec->clk_hdmi_cec)) {
|
|
ret = clk_prepare(cec->clk_hdmi_cec);
|
|
if (ret) {
|
|
- dev_err(&pdev->dev, "Unable to prepare hdmi-cec clock\n");
|
|
+ dev_err(&pdev->dev, "Can't prepare hdmi-cec clock\n");
|
|
return ret;
|
|
}
|
|
}
|
|
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
|
|
index 9392e3409fba0..7c8256441db33 100644
|
|
--- a/drivers/media/platform/stm32/stm32-dcmi.c
|
|
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
|
|
@@ -95,6 +95,9 @@ enum state {
|
|
#define MIN_HEIGHT 16U
|
|
#define MAX_HEIGHT 2592U
|
|
|
|
+/* DMA can sustain YUV 720p@15fps max */
|
|
+#define MAX_DMA_BANDWIDTH (1280 * 720 * 2 * 15)
|
|
+
|
|
#define TIMEOUT_MS 1000
|
|
|
|
#define OVERRUN_ERROR_THRESHOLD 3
|
|
@@ -324,7 +327,7 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
|
|
}
|
|
|
|
/*
|
|
- * Avoid call of dmaengine_terminate_all() between
|
|
+ * Avoid call of dmaengine_terminate_sync() between
|
|
* dmaengine_prep_slave_single() and dmaengine_submit()
|
|
* by locking the whole DMA submission sequence
|
|
*/
|
|
@@ -438,7 +441,7 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
|
|
}
|
|
|
|
/* Abort DMA operation */
|
|
- dmaengine_terminate_all(dcmi->dma_chan);
|
|
+ dmaengine_terminate_sync(dcmi->dma_chan);
|
|
|
|
/* Restart capture */
|
|
if (dcmi_restart_capture(dcmi))
|
|
@@ -733,7 +736,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
if (ret < 0) {
|
|
dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync (%d)\n",
|
|
__func__, ret);
|
|
- goto err_release_buffers;
|
|
+ goto err_pm_put;
|
|
}
|
|
|
|
ret = media_pipeline_start(&dcmi->vdev->entity, &dcmi->pipeline);
|
|
@@ -784,8 +787,31 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
dcmi_set_crop(dcmi);
|
|
|
|
/* Enable jpeg capture */
|
|
- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG)
|
|
- reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */
|
|
+ if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) {
|
|
+ unsigned int rate;
|
|
+ struct v4l2_streamparm p = {
|
|
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE
|
|
+ };
|
|
+ struct v4l2_fract frame_interval = {1, 30};
|
|
+
|
|
+ ret = v4l2_g_parm_cap(dcmi->vdev, dcmi->entity.source, &p);
|
|
+ if (!ret)
|
|
+ frame_interval = p.parm.capture.timeperframe;
|
|
+
|
|
+ rate = dcmi->fmt.fmt.pix.sizeimage *
|
|
+ frame_interval.denominator / frame_interval.numerator;
|
|
+
|
|
+ /*
|
|
+ * If rate exceed DMA capabilities, switch to snapshot mode
|
|
+ * to ensure that current DMA transfer is elapsed before
|
|
+ * capturing a new JPEG.
|
|
+ */
|
|
+ if (rate > MAX_DMA_BANDWIDTH) {
|
|
+ reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */
|
|
+ dev_dbg(dcmi->dev, "Capture rate is too high for continuous mode (%d > %d bytes/s), switch to snapshot mode\n",
|
|
+ rate, MAX_DMA_BANDWIDTH);
|
|
+ }
|
|
+ }
|
|
|
|
/* Enable dcmi */
|
|
reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
|
|
@@ -837,8 +863,6 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count)
|
|
|
|
err_pm_put:
|
|
pm_runtime_put(dcmi->dev);
|
|
-
|
|
-err_release_buffers:
|
|
spin_lock_irq(&dcmi->irqlock);
|
|
/*
|
|
* Return all buffers to vb2 in QUEUED state.
|
|
@@ -884,7 +908,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
|
|
|
|
/* Stop all pending DMA operations */
|
|
mutex_lock(&dcmi->dma_lock);
|
|
- dmaengine_terminate_all(dcmi->dma_chan);
|
|
+ dmaengine_terminate_sync(dcmi->dma_chan);
|
|
mutex_unlock(&dcmi->dma_lock);
|
|
|
|
pm_runtime_put(dcmi->dev);
|
|
@@ -1576,6 +1600,22 @@ static const struct dcmi_format dcmi_formats[] = {
|
|
.fourcc = V4L2_PIX_FMT_JPEG,
|
|
.mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
|
|
.bpp = 1,
|
|
+ }, {
|
|
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
|
|
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
|
|
+ .bpp = 1,
|
|
+ }, {
|
|
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
|
|
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
|
+ .bpp = 1,
|
|
+ }, {
|
|
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
|
|
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
|
+ .bpp = 1,
|
|
+ }, {
|
|
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
|
|
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
|
+ .bpp = 1,
|
|
},
|
|
};
|
|
|
|
@@ -1747,6 +1787,15 @@ static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier,
|
|
|
|
dev_dbg(dcmi->dev, "Subdev \"%s\" bound\n", subdev->name);
|
|
|
|
+ ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1);
|
|
+ if (ret) {
|
|
+ dev_err(dcmi->dev, "Failed to register video device\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_dbg(dcmi->dev, "Device registered as %s\n",
|
|
+ video_device_node_name(dcmi->vdev));
|
|
+
|
|
/*
|
|
* Link this sub-device to DCMI, it could be
|
|
* a parallel camera sensor or a bridge
|
|
@@ -1759,10 +1808,11 @@ static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier,
|
|
&dcmi->vdev->entity, 0,
|
|
MEDIA_LNK_FL_IMMUTABLE |
|
|
MEDIA_LNK_FL_ENABLED);
|
|
- if (ret)
|
|
+ if (ret) {
|
|
dev_err(dcmi->dev, "Failed to create media pad link with subdev \"%s\"\n",
|
|
subdev->name);
|
|
- else
|
|
+ video_unregister_device(dcmi->vdev);
|
|
+ } else
|
|
dev_dbg(dcmi->dev, "DCMI is now linked to \"%s\"\n",
|
|
subdev->name);
|
|
|
|
@@ -1853,7 +1903,9 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
|
|
dcmi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
|
if (IS_ERR(dcmi->rstc)) {
|
|
- dev_err(&pdev->dev, "Could not get reset control\n");
|
|
+ if (PTR_ERR(dcmi->rstc) != -EPROBE_DEFER)
|
|
+ dev_err(&pdev->dev, "Could not get reset control\n");
|
|
+
|
|
return PTR_ERR(dcmi->rstc);
|
|
}
|
|
|
|
@@ -1910,10 +1962,13 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
return PTR_ERR(mclk);
|
|
}
|
|
|
|
- chan = dma_request_slave_channel(&pdev->dev, "tx");
|
|
- if (!chan) {
|
|
- dev_info(&pdev->dev, "Unable to request DMA channel, defer probing\n");
|
|
- return -EPROBE_DEFER;
|
|
+ chan = dma_request_chan(&pdev->dev, "tx");
|
|
+ if (IS_ERR(chan)) {
|
|
+ ret = PTR_ERR(chan);
|
|
+ if (ret != -EPROBE_DEFER)
|
|
+ dev_err(&pdev->dev,
|
|
+ "Failed to request DMA channel: %d\n", ret);
|
|
+ return ret;
|
|
}
|
|
|
|
spin_lock_init(&dcmi->irqlock);
|
|
@@ -1971,15 +2026,6 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
}
|
|
dcmi->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
|
|
|
|
- ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1);
|
|
- if (ret) {
|
|
- dev_err(dcmi->dev, "Failed to register video device\n");
|
|
- goto err_media_entity_cleanup;
|
|
- }
|
|
-
|
|
- dev_dbg(dcmi->dev, "Device registered as %s\n",
|
|
- video_device_node_name(dcmi->vdev));
|
|
-
|
|
/* Buffer queue */
|
|
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
|
|
@@ -2000,7 +2046,7 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
|
|
ret = dcmi_graph_init(dcmi);
|
|
if (ret < 0)
|
|
- goto err_media_entity_cleanup;
|
|
+ goto err_vb2_queue_release;
|
|
|
|
/* Reset device */
|
|
ret = reset_control_assert(dcmi->rstc);
|
|
@@ -2026,7 +2072,10 @@ static int dcmi_probe(struct platform_device *pdev)
|
|
return 0;
|
|
|
|
err_cleanup:
|
|
+ v4l2_async_notifier_unregister(&dcmi->notifier);
|
|
v4l2_async_notifier_cleanup(&dcmi->notifier);
|
|
+err_vb2_queue_release:
|
|
+ vb2_queue_release(q);
|
|
err_media_entity_cleanup:
|
|
media_entity_cleanup(&dcmi->vdev->entity);
|
|
err_device_release:
|
|
@@ -2048,6 +2097,7 @@ static int dcmi_remove(struct platform_device *pdev)
|
|
|
|
v4l2_async_notifier_unregister(&dcmi->notifier);
|
|
v4l2_async_notifier_cleanup(&dcmi->notifier);
|
|
+ vb2_queue_release(&dcmi->queue);
|
|
media_entity_cleanup(&dcmi->vdev->entity);
|
|
v4l2_device_unregister(&dcmi->v4l2_dev);
|
|
media_device_cleanup(&dcmi->mdev);
|
|
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
|
|
index 3bd1888787eb3..7f370d6b7a143 100644
|
|
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
|
|
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
|
|
@@ -356,6 +356,9 @@ v4l2_fwnode_endpoint_parse_parallel_bus(struct fwnode_handle *fwnode,
|
|
pr_debug("data-enable-active %s\n", v ? "high" : "low");
|
|
}
|
|
|
|
+ if (!fwnode_property_read_u32(fwnode, "pclk-max-frequency", &v))
|
|
+ bus->pclk_max_frequency = v;
|
|
+
|
|
switch (bus_type) {
|
|
default:
|
|
bus->flags = flags;
|
|
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
|
|
index 833e04a7835c5..4ad5724f5cefd 100644
|
|
--- a/drivers/soc/Kconfig
|
|
+++ b/drivers/soc/Kconfig
|
|
@@ -14,6 +14,7 @@ source "drivers/soc/qcom/Kconfig"
|
|
source "drivers/soc/renesas/Kconfig"
|
|
source "drivers/soc/rockchip/Kconfig"
|
|
source "drivers/soc/samsung/Kconfig"
|
|
+source "drivers/soc/st/Kconfig"
|
|
source "drivers/soc/sunxi/Kconfig"
|
|
source "drivers/soc/tegra/Kconfig"
|
|
source "drivers/soc/ti/Kconfig"
|
|
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
|
|
index 2ec3550035243..30d3dcabf3bb0 100644
|
|
--- a/drivers/soc/Makefile
|
|
+++ b/drivers/soc/Makefile
|
|
@@ -20,6 +20,7 @@ obj-y += qcom/
|
|
obj-y += renesas/
|
|
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
|
obj-$(CONFIG_SOC_SAMSUNG) += samsung/
|
|
+obj-$(CONFIG_ARCH_STM32) += st/
|
|
obj-y += sunxi/
|
|
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
|
obj-y += ti/
|
|
diff --git a/drivers/soc/st/Kconfig b/drivers/soc/st/Kconfig
|
|
new file mode 100644
|
|
index 0000000000000..59db031505522
|
|
--- /dev/null
|
|
+++ b/drivers/soc/st/Kconfig
|
|
@@ -0,0 +1,17 @@
|
|
+if ARCH_STM32
|
|
+
|
|
+config STM32_HDP
|
|
+ bool "STMicroelectronics STM32MP157 Hardware Debug Port (HDP) pin control"
|
|
+ depends on MACH_STM32MP157
|
|
+ default n if MACH_STM32MP157
|
|
+ help
|
|
+ The Hardware Debug Port allows the observation of internal signals. By using multiplexers,
|
|
+ up to 16 signals for each of 8-bit output can be observed.
|
|
+
|
|
+config STM32_PM_DOMAINS
|
|
+ bool "STM32 PM domains"
|
|
+ depends on MACH_STM32MP157
|
|
+ select PM_GENERIC_DOMAINS
|
|
+ default y if MACH_STM32MP157
|
|
+
|
|
+endif # ARCH_STM32
|
|
diff --git a/drivers/soc/st/Makefile b/drivers/soc/st/Makefile
|
|
new file mode 100644
|
|
index 0000000000000..0fce1db166421
|
|
--- /dev/null
|
|
+++ b/drivers/soc/st/Makefile
|
|
@@ -0,0 +1,2 @@
|
|
+obj-$(CONFIG_STM32_HDP) += stm32_hdp.o
|
|
+obj-$(CONFIG_STM32_PM_DOMAINS) += stm32_pm_domain.o
|
|
diff --git a/drivers/soc/st/stm32_hdp.c b/drivers/soc/st/stm32_hdp.c
|
|
new file mode 100644
|
|
index 0000000000000..6408ac68ca5f8
|
|
--- /dev/null
|
|
+++ b/drivers/soc/st/stm32_hdp.c
|
|
@@ -0,0 +1,242 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Christophe Roullier <christophe.roullier@st.com>
|
|
+ * for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/pinctrl/pinconf.h>
|
|
+#include <linux/pinctrl/pinconf-generic.h>
|
|
+#include <linux/pinctrl/pinctrl.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/suspend.h>
|
|
+
|
|
+#define HDP_CTRL_ENABLE 1
|
|
+#define HDP_CTRL_DISABLE 0
|
|
+
|
|
+enum {
|
|
+ HDP_CTRL = 0,
|
|
+ HDP_MUX = 0x4,
|
|
+ HDP_VAL = 0x10,
|
|
+ HDP_GPOSET = 0x14,
|
|
+ HDP_GPOCLR = 0x18,
|
|
+ HDP_GPOVAL = 0x1c,
|
|
+ HDP_VERR = 0x3f4,
|
|
+ HDP_IPIDR = 0x3f8,
|
|
+ HDP_SIDR = 0x3fc
|
|
+} HDP_register_offsets;
|
|
+
|
|
+struct data_priv {
|
|
+ struct clk *clk;
|
|
+ int clk_is_enabled;
|
|
+ struct dentry *pwr_dentry;
|
|
+ unsigned char __iomem *hdp_membase;
|
|
+ unsigned int hdp_ctrl;
|
|
+ unsigned int hdp_mux;
|
|
+};
|
|
+
|
|
+/* enable/disable */
|
|
+static int stm32_hdp_enable_set(void *data, int val)
|
|
+{
|
|
+ struct data_priv *e = (struct data_priv *)data;
|
|
+
|
|
+ if (!e->clk)
|
|
+ return -EPERM;
|
|
+
|
|
+ if (val == 1) {
|
|
+ if (clk_prepare_enable(e->clk) < 0) {
|
|
+ pr_err("Failed to enable HDP clock\n");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ e->clk_is_enabled = 1;
|
|
+ } else {
|
|
+ clk_disable_unprepare(e->clk);
|
|
+ e->clk_is_enabled = 0;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_hdp_fops_set(void *data, u64 val)
|
|
+{
|
|
+ unsigned char __iomem *addr = (unsigned char __iomem *)data;
|
|
+
|
|
+ writel_relaxed(val, addr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_hdp_fops_get(void *data, u64 *val)
|
|
+{
|
|
+ unsigned char __iomem *addr = (unsigned char __iomem *)data;
|
|
+
|
|
+ *val = readl_relaxed(addr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+DEFINE_SIMPLE_ATTRIBUTE(stm32_hdp_fops, stm32_hdp_fops_get,
|
|
+ stm32_hdp_fops_set, "0x%llx\n");
|
|
+
|
|
+int stm32_hdp_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device_node *np = pdev->dev.of_node;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct resource *res;
|
|
+
|
|
+ struct data_priv *data;
|
|
+ struct dentry *r;
|
|
+
|
|
+ int ret;
|
|
+ const __be32 *getmuxing;
|
|
+ u32 muxing, version;
|
|
+
|
|
+ if (!np)
|
|
+ return -ENODEV;
|
|
+
|
|
+ data = devm_kzalloc(&pdev->dev, sizeof(struct data_priv), GFP_KERNEL);
|
|
+ if (!data)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ data->hdp_membase = devm_ioremap_resource(&pdev->dev, res);
|
|
+ if (IS_ERR(data->hdp_membase))
|
|
+ return PTR_ERR(data->hdp_membase);
|
|
+
|
|
+ /* Get HDP clocks */
|
|
+ data->clk = devm_clk_get(dev, "hdp");
|
|
+ if (IS_ERR(data->clk)) {
|
|
+ dev_err(dev, "No HDP CK clock provided...\n");
|
|
+ return PTR_ERR(data->clk);
|
|
+ }
|
|
+
|
|
+ /* Enable clock */
|
|
+ ret = stm32_hdp_enable_set(data, 1);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ getmuxing = of_get_property(np, "muxing-hdp", NULL);
|
|
+ if (!getmuxing) {
|
|
+ dev_err(dev,
|
|
+ "no muxing-hdp property in node\n");
|
|
+ /* Disable clock */
|
|
+ ret = stm32_hdp_enable_set(data, 0);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* add hdp directory */
|
|
+ r = debugfs_create_dir("hdp", NULL);
|
|
+ if (!r) {
|
|
+ dev_err(dev, "Unable to create HDP debugFS\n");
|
|
+ /* Disable clock */
|
|
+ ret = stm32_hdp_enable_set(data, 0);
|
|
+ if (ret != 0)
|
|
+ return ret;
|
|
+
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ debugfs_create_file("ctrl", 0644, r,
|
|
+ data->hdp_membase + HDP_CTRL, &stm32_hdp_fops);
|
|
+ debugfs_create_file("mux", 0644, r,
|
|
+ data->hdp_membase + HDP_MUX, &stm32_hdp_fops);
|
|
+ debugfs_create_file("val", 0644, r,
|
|
+ data->hdp_membase + HDP_VAL, &stm32_hdp_fops);
|
|
+ debugfs_create_file("gposet", 0644, r,
|
|
+ data->hdp_membase + HDP_GPOSET, &stm32_hdp_fops);
|
|
+ debugfs_create_file("gpoclr", 0644, r,
|
|
+ data->hdp_membase + HDP_GPOCLR, &stm32_hdp_fops);
|
|
+ debugfs_create_file("gpoval", 0644, r,
|
|
+ data->hdp_membase + HDP_GPOVAL, &stm32_hdp_fops);
|
|
+
|
|
+ /* Enable HDP */
|
|
+ writel(HDP_CTRL_ENABLE, data->hdp_membase + HDP_CTRL);
|
|
+
|
|
+ /* HDP Multiplexing */
|
|
+ muxing = of_read_number(getmuxing,
|
|
+ of_n_addr_cells(np));
|
|
+
|
|
+ writel(muxing, data->hdp_membase + HDP_MUX);
|
|
+
|
|
+ platform_set_drvdata(pdev, data);
|
|
+
|
|
+ /* Get Majeur, Minor version */
|
|
+ version = readl(data->hdp_membase + HDP_VERR);
|
|
+
|
|
+ dev_info(dev, "STM32 HDP version %d.%d initialized\n",
|
|
+ version >> 4, version & 0x0F);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_hdp_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct data_priv *data = platform_get_drvdata(pdev);
|
|
+
|
|
+ /* Disable HDP */
|
|
+ writel(HDP_CTRL_DISABLE, data->hdp_membase + HDP_CTRL);
|
|
+
|
|
+ if (data->clk) {
|
|
+ if (data->clk_is_enabled)
|
|
+ clk_disable_unprepare(data->clk);
|
|
+ }
|
|
+
|
|
+ pr_info("driver STM32 HDP removed\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PM_SLEEP
|
|
+static int stm32_hdp_suspend(struct device *dev)
|
|
+{
|
|
+ struct data_priv *data = dev_get_drvdata(dev);
|
|
+
|
|
+ data->hdp_ctrl = readl_relaxed(data->hdp_membase + HDP_CTRL);
|
|
+ data->hdp_mux = readl_relaxed(data->hdp_membase + HDP_MUX);
|
|
+
|
|
+ pinctrl_pm_select_sleep_state(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_hdp_resume(struct device *dev)
|
|
+{
|
|
+ struct data_priv *data = dev_get_drvdata(dev);
|
|
+
|
|
+ writel_relaxed(data->hdp_ctrl, data->hdp_membase + HDP_CTRL);
|
|
+ writel_relaxed(data->hdp_mux, data->hdp_membase + HDP_MUX);
|
|
+
|
|
+ pinctrl_pm_select_default_state(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#endif /* CONFIG_PM_SLEEP */
|
|
+
|
|
+static SIMPLE_DEV_PM_OPS(stm32_hdp_pm_ops,
|
|
+ stm32_hdp_suspend,
|
|
+ stm32_hdp_resume);
|
|
+
|
|
+static const struct of_device_id hdp_match[] = {
|
|
+ { .compatible = "st,stm32mp1-hdp",},
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, hdp_match);
|
|
+
|
|
+static struct platform_driver hdp_driver = {
|
|
+ .probe = stm32_hdp_probe,
|
|
+ .remove = stm32_hdp_remove,
|
|
+ .driver = {
|
|
+ .name = "hdp",
|
|
+ .of_match_table = hdp_match,
|
|
+ .pm = &stm32_hdp_pm_ops,
|
|
+ },
|
|
+};
|
|
+
|
|
+module_platform_driver(hdp_driver);
|
|
diff --git a/drivers/soc/st/stm32_pm_domain.c b/drivers/soc/st/stm32_pm_domain.c
|
|
new file mode 100644
|
|
index 0000000000000..0386624c20f2f
|
|
--- /dev/null
|
|
+++ b/drivers/soc/st/stm32_pm_domain.c
|
|
@@ -0,0 +1,212 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Alexandre Torgue <alexandre.torgue@st.com> for STMicroelectronics.
|
|
+ * Author: Olivier Bideau <olivier.bideau@st.com> for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#include <linux/arm-smccc.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/pm_domain.h>
|
|
+#include <linux/printk.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#define SMC(domain, state) \
|
|
+{ \
|
|
+ struct arm_smccc_res res; \
|
|
+ arm_smccc_smc(0x82001008, domain, state, 0, \
|
|
+ 0, 0, 0, 0, &res); \
|
|
+}
|
|
+
|
|
+#define STM32_SMC_PD_DOMAIN_ON 0
|
|
+#define STM32_SMC_PD_DOMAIN_OFF 1
|
|
+
|
|
+struct stm32_pm_domain {
|
|
+ struct device *dev;
|
|
+ struct generic_pm_domain genpd;
|
|
+ int id;
|
|
+};
|
|
+
|
|
+static int stm32_pd_power_off(struct generic_pm_domain *domain)
|
|
+{
|
|
+ struct stm32_pm_domain *priv = container_of(domain,
|
|
+ struct stm32_pm_domain,
|
|
+ genpd);
|
|
+
|
|
+ SMC(priv->id, STM32_SMC_PD_DOMAIN_OFF);
|
|
+
|
|
+ dev_dbg(priv->dev, "%s OFF\n", domain->name);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int stm32_pd_power_on(struct generic_pm_domain *domain)
|
|
+{
|
|
+ struct stm32_pm_domain *priv = container_of(domain,
|
|
+ struct stm32_pm_domain,
|
|
+ genpd);
|
|
+
|
|
+ SMC(priv->id, STM32_SMC_PD_DOMAIN_ON);
|
|
+
|
|
+ dev_dbg(priv->dev, "%s ON\n", domain->name);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void stm32_pm_domain_remove(struct stm32_pm_domain *domain)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = pm_genpd_remove(&domain->genpd);
|
|
+ if (ret)
|
|
+ dev_err(domain->dev, "failed to remove PM domain %s: %d\n",
|
|
+ domain->genpd.name, ret);
|
|
+}
|
|
+
|
|
+static int stm32_pm_domain_add(struct stm32_pm_domain *domain,
|
|
+ struct device *dev,
|
|
+ struct device_node *np)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ domain->dev = dev;
|
|
+ domain->genpd.name = np->name;
|
|
+ domain->genpd.power_off = stm32_pd_power_off;
|
|
+ domain->genpd.power_on = stm32_pd_power_on;
|
|
+ domain->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
|
|
+
|
|
+ ret = of_property_read_u32(np, "reg", &domain->id);
|
|
+ if (ret) {
|
|
+ dev_err(domain->dev, "no domain ID\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = pm_genpd_init(&domain->genpd, NULL, 0);
|
|
+ if (ret < 0) {
|
|
+ dev_err(domain->dev, "failed to initialise PM domain %s: %d\n",
|
|
+ np->name, ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = of_genpd_add_provider_simple(np, &domain->genpd);
|
|
+ if (ret < 0) {
|
|
+ dev_err(domain->dev, "failed to register PM domain %s: %d\n",
|
|
+ np->name, ret);
|
|
+ stm32_pm_domain_remove(domain);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_info(domain->dev, "domain %s registered\n", np->name);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void stm32_pm_subdomain_add(struct stm32_pm_domain *domain,
|
|
+ struct device *dev,
|
|
+ struct device_node *np)
|
|
+{
|
|
+ struct device_node *np_child;
|
|
+ int ret;
|
|
+
|
|
+ for_each_child_of_node(np, np_child) {
|
|
+ struct stm32_pm_domain *sub_domain;
|
|
+
|
|
+ sub_domain = devm_kzalloc(dev, sizeof(*sub_domain), GFP_KERNEL);
|
|
+ if (!sub_domain)
|
|
+ continue;
|
|
+
|
|
+ sub_domain->dev = dev;
|
|
+ sub_domain->genpd.name = np_child->name;
|
|
+ sub_domain->genpd.power_off = stm32_pd_power_off;
|
|
+ sub_domain->genpd.power_on = stm32_pd_power_on;
|
|
+ sub_domain->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
|
|
+
|
|
+ ret = of_property_read_u32(np_child, "reg", &sub_domain->id);
|
|
+ if (ret) {
|
|
+ dev_err(sub_domain->dev, "no domain ID\n");
|
|
+ devm_kfree(dev, sub_domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = pm_genpd_init(&sub_domain->genpd, NULL, 0);
|
|
+ if (ret < 0) {
|
|
+ dev_err(sub_domain->dev, "failed to initialise PM domain %s: %d\n"
|
|
+ , np_child->name, ret);
|
|
+ devm_kfree(dev, sub_domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = of_genpd_add_provider_simple(np_child,
|
|
+ &sub_domain->genpd);
|
|
+ if (ret < 0) {
|
|
+ dev_err(sub_domain->dev, "failed to register PM domain %s: %d\n"
|
|
+ , np_child->name, ret);
|
|
+ stm32_pm_domain_remove(sub_domain);
|
|
+ devm_kfree(dev, sub_domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret = pm_genpd_add_subdomain(&domain->genpd,
|
|
+ &sub_domain->genpd);
|
|
+
|
|
+ if (ret < 0) {
|
|
+ dev_err(sub_domain->dev, "failed to add Sub PM domain %s: %d\n"
|
|
+ , np_child->name, ret);
|
|
+ stm32_pm_domain_remove(sub_domain);
|
|
+ devm_kfree(dev, sub_domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ dev_info(sub_domain->dev, "subdomain %s registered\n",
|
|
+ np_child->name);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int stm32_pm_domain_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *np = dev->of_node, *child_np;
|
|
+ int ret;
|
|
+
|
|
+ for_each_child_of_node(np, child_np) {
|
|
+ struct stm32_pm_domain *domain;
|
|
+
|
|
+ domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL);
|
|
+ if (!domain)
|
|
+ continue;
|
|
+
|
|
+ ret = stm32_pm_domain_add(domain, dev, child_np);
|
|
+ if (ret) {
|
|
+ devm_kfree(dev, domain);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ stm32_pm_subdomain_add(domain, dev, child_np);
|
|
+ }
|
|
+
|
|
+ dev_info(dev, "domains probed\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id stm32_pm_domain_matches[] = {
|
|
+ { .compatible = "st,stm32mp157c-pd", },
|
|
+ { },
|
|
+};
|
|
+
|
|
+static struct platform_driver stm32_pm_domains_driver = {
|
|
+ .probe = stm32_pm_domain_probe,
|
|
+ .driver = {
|
|
+ .name = "stm32-pm-domain",
|
|
+ .of_match_table = stm32_pm_domain_matches,
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init stm32_pm_domains_init(void)
|
|
+{
|
|
+ return platform_driver_register(&stm32_pm_domains_driver);
|
|
+}
|
|
+core_initcall(stm32_pm_domains_init);
|
|
diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c
|
|
index cf9ddc52f30e1..1cecf95449c3e 100644
|
|
--- a/drivers/thermal/st/stm_thermal.c
|
|
+++ b/drivers/thermal/st/stm_thermal.c
|
|
@@ -30,7 +30,7 @@
|
|
#define DTS_DR_OFFSET 0x1C
|
|
#define DTS_SR_OFFSET 0x20
|
|
#define DTS_ITENR_OFFSET 0x24
|
|
-#define DTS_CIFR_OFFSET 0x28
|
|
+#define DTS_ICIFR_OFFSET 0x28
|
|
|
|
/* DTS_CFGR1 register mask definitions */
|
|
#define HSREF_CLK_DIV_MASK GENMASK(30, 24)
|
|
@@ -51,10 +51,16 @@
|
|
/* DTS_DR register mask definitions */
|
|
#define TS1_MFREQ_MASK GENMASK(15, 0)
|
|
|
|
+/* DTS_ITENR register mask definitions */
|
|
+#define ITENR_MASK (GENMASK(2, 0) | GENMASK(6, 4))
|
|
+
|
|
+/* DTS_ICIFR register mask definitions */
|
|
+#define ICIFR_MASK (GENMASK(2, 0) | GENMASK(6, 4))
|
|
+
|
|
/* Less significant bit position definitions */
|
|
#define TS1_T0_POS 16
|
|
-#define TS1_SMP_TIME_POS 16
|
|
#define TS1_HITTHD_POS 16
|
|
+#define TS1_LITTHD_POS 0
|
|
#define HSREF_CLK_DIV_POS 24
|
|
|
|
/* DTS_CFGR1 bit definitions */
|
|
@@ -76,58 +82,37 @@
|
|
#define ONE_MHZ 1000000
|
|
#define POLL_TIMEOUT 5000
|
|
#define STARTUP_TIME 40
|
|
-#define TS1_T0_VAL0 30
|
|
-#define TS1_T0_VAL1 130
|
|
+#define T0 30000 /* 30 celsius */
|
|
#define NO_HW_TRIG 0
|
|
-
|
|
-/* The Thermal Framework expects millidegrees */
|
|
-#define mcelsius(temp) ((temp) * 1000)
|
|
-
|
|
-/* The Sensor expects oC degrees */
|
|
-#define celsius(temp) ((temp) / 1000)
|
|
+#define SAMPLING_TIME 15
|
|
|
|
struct stm_thermal_sensor {
|
|
struct device *dev;
|
|
struct thermal_zone_device *th_dev;
|
|
enum thermal_device_mode mode;
|
|
struct clk *clk;
|
|
- int high_temp;
|
|
- int low_temp;
|
|
- int temp_critical;
|
|
- int temp_passive;
|
|
unsigned int low_temp_enabled;
|
|
- int num_trips;
|
|
+ unsigned int high_temp_enabled;
|
|
int irq;
|
|
- unsigned int irq_enabled;
|
|
void __iomem *base;
|
|
- int t0, fmt0, ramp_coeff;
|
|
+ int fmt0, ramp_coeff;
|
|
};
|
|
|
|
-static irqreturn_t stm_thermal_alarm_irq(int irq, void *sdata)
|
|
-{
|
|
- struct stm_thermal_sensor *sensor = sdata;
|
|
-
|
|
- disable_irq_nosync(irq);
|
|
- sensor->irq_enabled = false;
|
|
-
|
|
- return IRQ_WAKE_THREAD;
|
|
-}
|
|
+static int stm_enable_irq(struct stm_thermal_sensor *sensor);
|
|
|
|
static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata)
|
|
{
|
|
- u32 value;
|
|
struct stm_thermal_sensor *sensor = sdata;
|
|
|
|
- /* read IT reason in SR and clear flags */
|
|
- value = readl_relaxed(sensor->base + DTS_SR_OFFSET);
|
|
+ dev_dbg(sensor->dev, "sr:%d\n",
|
|
+ readl_relaxed(sensor->base + DTS_SR_OFFSET));
|
|
|
|
- if ((value & LOW_THRESHOLD) == LOW_THRESHOLD)
|
|
- writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
|
+ thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED);
|
|
|
|
- if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD)
|
|
- writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
|
+ stm_enable_irq(sensor);
|
|
|
|
- thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED);
|
|
+ /* Acknoledge all DTS irqs */
|
|
+ writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
@@ -160,6 +145,8 @@ static int stm_sensor_power_on(struct stm_thermal_sensor *sensor)
|
|
writel_relaxed(value, sensor->base +
|
|
DTS_CFGR1_OFFSET);
|
|
|
|
+ sensor->mode = THERMAL_DEVICE_ENABLED;
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -167,6 +154,8 @@ static int stm_sensor_power_off(struct stm_thermal_sensor *sensor)
|
|
{
|
|
u32 value;
|
|
|
|
+ sensor->mode = THERMAL_DEVICE_DISABLED;
|
|
+
|
|
/* Stop measuring */
|
|
value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET);
|
|
value &= ~TS1_START;
|
|
@@ -232,14 +221,6 @@ static int stm_thermal_calibration(struct stm_thermal_sensor *sensor)
|
|
/* Fill in DTS structure with factory sensor values */
|
|
static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor)
|
|
{
|
|
- /* Retrieve engineering calibration temperature */
|
|
- sensor->t0 = readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET) &
|
|
- TS1_T0_MASK;
|
|
- if (!sensor->t0)
|
|
- sensor->t0 = TS1_T0_VAL0;
|
|
- else
|
|
- sensor->t0 = TS1_T0_VAL1;
|
|
-
|
|
/* Retrieve fmt0 and put it on Hz */
|
|
sensor->fmt0 = ADJUST * (readl_relaxed(sensor->base +
|
|
DTS_T0VALR1_OFFSET) & TS1_FMT0_MASK);
|
|
@@ -253,8 +234,8 @@ static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor)
|
|
return -EINVAL;
|
|
}
|
|
|
|
- dev_dbg(sensor->dev, "%s: T0 = %doC, FMT0 = %dHz, RAMP_COEFF = %dHz/oC",
|
|
- __func__, sensor->t0, sensor->fmt0, sensor->ramp_coeff);
|
|
+ dev_dbg(sensor->dev, "%s: FMT0 = %dHz, RAMP_COEFF = %dHz/oC",
|
|
+ __func__, sensor->fmt0, sensor->ramp_coeff);
|
|
|
|
return 0;
|
|
}
|
|
@@ -263,60 +244,16 @@ static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor,
|
|
int temp, u32 *th)
|
|
{
|
|
int freqM;
|
|
- u32 sampling_time;
|
|
-
|
|
- /* Retrieve the number of periods to sample */
|
|
- sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) &
|
|
- TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS;
|
|
|
|
/* Figure out the CLK_PTAT frequency for a given temperature */
|
|
- freqM = ((temp - sensor->t0) * sensor->ramp_coeff)
|
|
- + sensor->fmt0;
|
|
-
|
|
- dev_dbg(sensor->dev, "%s: freqM for threshold = %d Hz",
|
|
- __func__, freqM);
|
|
+ freqM = ((temp - T0) * sensor->ramp_coeff) / 1000 + sensor->fmt0;
|
|
|
|
/* Figure out the threshold sample number */
|
|
- *th = clk_get_rate(sensor->clk);
|
|
+ *th = clk_get_rate(sensor->clk) * SAMPLING_TIME / freqM;
|
|
if (!*th)
|
|
return -EINVAL;
|
|
|
|
- *th = *th / freqM;
|
|
-
|
|
- *th *= sampling_time;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int stm_thermal_set_threshold(struct stm_thermal_sensor *sensor)
|
|
-{
|
|
- u32 value, th;
|
|
- int ret;
|
|
-
|
|
- value = readl_relaxed(sensor->base + DTS_ITR1_OFFSET);
|
|
-
|
|
- /* Erase threshold content */
|
|
- value &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK);
|
|
-
|
|
- /* Retrieve the sample threshold number th for a given temperature */
|
|
- ret = stm_thermal_calculate_threshold(sensor, sensor->high_temp, &th);
|
|
- if (ret)
|
|
- return ret;
|
|
-
|
|
- value |= th & TS1_LITTHD_MASK;
|
|
-
|
|
- if (sensor->low_temp_enabled) {
|
|
- /* Retrieve the sample threshold */
|
|
- ret = stm_thermal_calculate_threshold(sensor, sensor->low_temp,
|
|
- &th);
|
|
- if (ret)
|
|
- return ret;
|
|
-
|
|
- value |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS));
|
|
- }
|
|
-
|
|
- /* Write value on the Low interrupt threshold */
|
|
- writel_relaxed(value, sensor->base + DTS_ITR1_OFFSET);
|
|
+ dev_dbg(sensor->dev, "freqM=%d Hz, threshold=0x%x", freqM, *th);
|
|
|
|
return 0;
|
|
}
|
|
@@ -326,12 +263,10 @@ static int stm_disable_irq(struct stm_thermal_sensor *sensor)
|
|
{
|
|
u32 value;
|
|
|
|
- /* Disable IT generation for low and high thresholds */
|
|
+ /* Disable IT generation */
|
|
value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
|
|
- writel_relaxed(value & ~(LOW_THRESHOLD | HIGH_THRESHOLD),
|
|
- sensor->base + DTS_ITENR_OFFSET);
|
|
-
|
|
- dev_dbg(sensor->dev, "%s: IT disabled on sensor side", __func__);
|
|
+ value &= ~ITENR_MASK;
|
|
+ writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET);
|
|
|
|
return 0;
|
|
}
|
|
@@ -341,62 +276,68 @@ static int stm_enable_irq(struct stm_thermal_sensor *sensor)
|
|
{
|
|
u32 value;
|
|
|
|
- /*
|
|
- * Code below enables High temperature threshold using a low threshold
|
|
- * sampling value
|
|
- */
|
|
-
|
|
- /* Make sure LOW_THRESHOLD IT is clear before enabling */
|
|
- writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
|
+ dev_dbg(sensor->dev, "low:%d high:%d\n", sensor->low_temp_enabled,
|
|
+ sensor->high_temp_enabled);
|
|
|
|
- /* Enable IT generation for low threshold */
|
|
+ /* Disable IT generation for low and high thresholds */
|
|
value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET);
|
|
- value |= LOW_THRESHOLD;
|
|
-
|
|
- /* Enable the low temperature threshold if needed */
|
|
- if (sensor->low_temp_enabled) {
|
|
- /* Make sure HIGH_THRESHOLD IT is clear before enabling */
|
|
- writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET);
|
|
+ value &= ~(LOW_THRESHOLD | HIGH_THRESHOLD);
|
|
|
|
- /* Enable IT generation for high threshold */
|
|
+ if (sensor->low_temp_enabled)
|
|
value |= HIGH_THRESHOLD;
|
|
- }
|
|
|
|
- /* Enable thresholds */
|
|
- writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET);
|
|
+ if (sensor->high_temp_enabled)
|
|
+ value |= LOW_THRESHOLD;
|
|
|
|
- dev_dbg(sensor->dev, "%s: IT enabled on sensor side", __func__);
|
|
+ /* Enable interrupts */
|
|
+ writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET);
|
|
|
|
return 0;
|
|
}
|
|
|
|
-static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor)
|
|
+static int stm_thermal_set_trips(void *data, int low, int high)
|
|
{
|
|
+ struct stm_thermal_sensor *sensor = data;
|
|
+ u32 itr1, th;
|
|
int ret;
|
|
|
|
- sensor->mode = THERMAL_DEVICE_DISABLED;
|
|
+ dev_dbg(sensor->dev, "set trips %d <--> %d\n", low, high);
|
|
|
|
- ret = stm_sensor_power_off(sensor);
|
|
- if (ret)
|
|
- return ret;
|
|
+ /* Erase threshold content */
|
|
+ itr1 = readl_relaxed(sensor->base + DTS_ITR1_OFFSET);
|
|
+ itr1 &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK);
|
|
|
|
- ret = stm_disable_irq(sensor);
|
|
- if (ret)
|
|
- return ret;
|
|
+ /*
|
|
+ * Disable low-temp if "low" is too small. As per thermal framework
|
|
+ * API, we use -INT_MAX rather than INT_MIN.
|
|
+ */
|
|
|
|
- ret = stm_thermal_set_threshold(sensor);
|
|
- if (ret)
|
|
- return ret;
|
|
+ if (low > -INT_MAX) {
|
|
+ sensor->low_temp_enabled = 1;
|
|
+ /* add 0.5 of hysteresis due to measurement error */
|
|
+ ret = stm_thermal_calculate_threshold(sensor, low - 500, &th);
|
|
+ if (ret)
|
|
+ return ret;
|
|
|
|
- ret = stm_enable_irq(sensor);
|
|
- if (ret)
|
|
- return ret;
|
|
+ itr1 |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS));
|
|
+ } else {
|
|
+ sensor->low_temp_enabled = 0;
|
|
+ }
|
|
|
|
- ret = stm_sensor_power_on(sensor);
|
|
- if (ret)
|
|
- return ret;
|
|
+ /* Disable high-temp if "high" is too big. */
|
|
+ if (high < INT_MAX) {
|
|
+ sensor->high_temp_enabled = 1;
|
|
+ ret = stm_thermal_calculate_threshold(sensor, high, &th);
|
|
+ if (ret)
|
|
+ return ret;
|
|
|
|
- sensor->mode = THERMAL_DEVICE_ENABLED;
|
|
+ itr1 |= (TS1_LITTHD_MASK & (th << TS1_LITTHD_POS));
|
|
+ } else {
|
|
+ sensor->high_temp_enabled = 0;
|
|
+ }
|
|
+
|
|
+ /* Write new threshod values*/
|
|
+ writel_relaxed(itr1, sensor->base + DTS_ITR1_OFFSET);
|
|
|
|
return 0;
|
|
}
|
|
@@ -405,76 +346,26 @@ static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor)
|
|
static int stm_thermal_get_temp(void *data, int *temp)
|
|
{
|
|
struct stm_thermal_sensor *sensor = data;
|
|
- u32 sampling_time;
|
|
+ u32 periods;
|
|
int freqM, ret;
|
|
|
|
if (sensor->mode != THERMAL_DEVICE_ENABLED)
|
|
return -EAGAIN;
|
|
|
|
- /* Retrieve the number of samples */
|
|
- ret = readl_poll_timeout(sensor->base + DTS_DR_OFFSET, freqM,
|
|
- (freqM & TS1_MFREQ_MASK), STARTUP_TIME,
|
|
- POLL_TIMEOUT);
|
|
-
|
|
+ /* Retrieve the number of periods sampled */
|
|
+ ret = readl_relaxed_poll_timeout(sensor->base + DTS_DR_OFFSET, periods,
|
|
+ (periods & TS1_MFREQ_MASK),
|
|
+ STARTUP_TIME, POLL_TIMEOUT);
|
|
if (ret)
|
|
return ret;
|
|
|
|
- if (!freqM)
|
|
- return -ENODATA;
|
|
-
|
|
- /* Retrieve the number of periods sampled */
|
|
- sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) &
|
|
- TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS;
|
|
-
|
|
- /* Figure out the number of samples per period */
|
|
- freqM /= sampling_time;
|
|
-
|
|
/* Figure out the CLK_PTAT frequency */
|
|
- freqM = clk_get_rate(sensor->clk) / freqM;
|
|
+ freqM = (clk_get_rate(sensor->clk) * SAMPLING_TIME) / periods;
|
|
if (!freqM)
|
|
return -EINVAL;
|
|
|
|
- dev_dbg(sensor->dev, "%s: freqM=%d\n", __func__, freqM);
|
|
-
|
|
/* Figure out the temperature in mili celsius */
|
|
- *temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) /
|
|
- sensor->ramp_coeff));
|
|
-
|
|
- dev_dbg(sensor->dev, "%s: temperature = %d millicelsius",
|
|
- __func__, *temp);
|
|
-
|
|
- /* Update thresholds */
|
|
- if (sensor->num_trips > 1) {
|
|
- /* Update alarm threshold value to next higher trip point */
|
|
- if (sensor->high_temp == sensor->temp_passive &&
|
|
- celsius(*temp) >= sensor->temp_passive) {
|
|
- sensor->high_temp = sensor->temp_critical;
|
|
- sensor->low_temp = sensor->temp_passive;
|
|
- sensor->low_temp_enabled = true;
|
|
- ret = stm_thermal_update_threshold(sensor);
|
|
- if (ret)
|
|
- return ret;
|
|
- }
|
|
-
|
|
- if (sensor->high_temp == sensor->temp_critical &&
|
|
- celsius(*temp) < sensor->temp_passive) {
|
|
- sensor->high_temp = sensor->temp_passive;
|
|
- sensor->low_temp_enabled = false;
|
|
- ret = stm_thermal_update_threshold(sensor);
|
|
- if (ret)
|
|
- return ret;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Re-enable alarm IRQ if temperature below critical
|
|
- * temperature
|
|
- */
|
|
- if (!sensor->irq_enabled &&
|
|
- (celsius(*temp) < sensor->temp_critical)) {
|
|
- sensor->irq_enabled = true;
|
|
- enable_irq(sensor->irq);
|
|
- }
|
|
- }
|
|
+ *temp = (freqM - sensor->fmt0) * 1000 / sensor->ramp_coeff + T0;
|
|
|
|
return 0;
|
|
}
|
|
@@ -493,7 +384,7 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor)
|
|
}
|
|
|
|
ret = devm_request_threaded_irq(dev, sensor->irq,
|
|
- stm_thermal_alarm_irq,
|
|
+ NULL,
|
|
stm_thermal_alarm_irq_thread,
|
|
IRQF_ONESHOT,
|
|
dev->driver->name, sensor);
|
|
@@ -503,8 +394,6 @@ static int stm_register_irq(struct stm_thermal_sensor *sensor)
|
|
return ret;
|
|
}
|
|
|
|
- sensor->irq_enabled = true;
|
|
-
|
|
dev_dbg(dev, "%s: thermal IRQ registered", __func__);
|
|
|
|
return 0;
|
|
@@ -514,6 +403,8 @@ static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor)
|
|
{
|
|
int ret;
|
|
|
|
+ stm_disable_irq(sensor);
|
|
+
|
|
ret = stm_sensor_power_off(sensor);
|
|
if (ret)
|
|
return ret;
|
|
@@ -526,7 +417,6 @@ static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor)
|
|
static int stm_thermal_prepare(struct stm_thermal_sensor *sensor)
|
|
{
|
|
int ret;
|
|
- struct device *dev = sensor->dev;
|
|
|
|
ret = clk_prepare_enable(sensor->clk);
|
|
if (ret)
|
|
@@ -540,26 +430,8 @@ static int stm_thermal_prepare(struct stm_thermal_sensor *sensor)
|
|
if (ret)
|
|
goto thermal_unprepare;
|
|
|
|
- /* Set threshold(s) for IRQ */
|
|
- ret = stm_thermal_set_threshold(sensor);
|
|
- if (ret)
|
|
- goto thermal_unprepare;
|
|
-
|
|
- ret = stm_enable_irq(sensor);
|
|
- if (ret)
|
|
- goto thermal_unprepare;
|
|
-
|
|
- ret = stm_sensor_power_on(sensor);
|
|
- if (ret) {
|
|
- dev_err(dev, "%s: failed to power on sensor\n", __func__);
|
|
- goto irq_disable;
|
|
- }
|
|
-
|
|
return 0;
|
|
|
|
-irq_disable:
|
|
- stm_disable_irq(sensor);
|
|
-
|
|
thermal_unprepare:
|
|
clk_disable_unprepare(sensor->clk);
|
|
|
|
@@ -576,8 +448,6 @@ static int stm_thermal_suspend(struct device *dev)
|
|
if (ret)
|
|
return ret;
|
|
|
|
- sensor->mode = THERMAL_DEVICE_DISABLED;
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -590,7 +460,12 @@ static int stm_thermal_resume(struct device *dev)
|
|
if (ret)
|
|
return ret;
|
|
|
|
- sensor->mode = THERMAL_DEVICE_ENABLED;
|
|
+ ret = stm_sensor_power_on(sensor);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED);
|
|
+ stm_enable_irq(sensor);
|
|
|
|
return 0;
|
|
}
|
|
@@ -600,6 +475,7 @@ SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume);
|
|
|
|
static const struct thermal_zone_of_device_ops stm_tz_ops = {
|
|
.get_temp = stm_thermal_get_temp,
|
|
+ .set_trips = stm_thermal_set_trips,
|
|
};
|
|
|
|
static const struct of_device_id stm_thermal_of_match[] = {
|
|
@@ -612,9 +488,8 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|
{
|
|
struct stm_thermal_sensor *sensor;
|
|
struct resource *res;
|
|
- const struct thermal_trip *trip;
|
|
void __iomem *base;
|
|
- int ret, i;
|
|
+ int ret;
|
|
|
|
if (!pdev->dev.of_node) {
|
|
dev_err(&pdev->dev, "%s: device tree node not found\n",
|
|
@@ -640,15 +515,28 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|
|
|
sensor->clk = devm_clk_get(&pdev->dev, "pclk");
|
|
if (IS_ERR(sensor->clk)) {
|
|
- dev_err(&pdev->dev, "%s: failed to fetch PCLK clock\n",
|
|
- __func__);
|
|
+ if (PTR_ERR(sensor->clk) != -EPROBE_DEFER)
|
|
+ dev_err(&pdev->dev, "Failed to get PCLK clock\n");
|
|
return PTR_ERR(sensor->clk);
|
|
}
|
|
|
|
- /* Register IRQ into GIC */
|
|
- ret = stm_register_irq(sensor);
|
|
- if (ret)
|
|
+ stm_disable_irq(sensor);
|
|
+
|
|
+ /* Clear irq flags */
|
|
+ writel_relaxed(ICIFR_MASK, sensor->base + DTS_ICIFR_OFFSET);
|
|
+
|
|
+ /* Configure and enable HW sensor */
|
|
+ ret = stm_thermal_prepare(sensor);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "Error preprare sensor: %d\n", ret);
|
|
return ret;
|
|
+ }
|
|
+
|
|
+ ret = stm_sensor_power_on(sensor);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "Error power on sensor: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
|
|
sensor->th_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
|
|
sensor,
|
|
@@ -661,53 +549,12 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|
return ret;
|
|
}
|
|
|
|
- if (!sensor->th_dev->ops->get_crit_temp) {
|
|
- /* Critical point must be provided */
|
|
- ret = -EINVAL;
|
|
- goto err_tz;
|
|
- }
|
|
-
|
|
- ret = sensor->th_dev->ops->get_crit_temp(sensor->th_dev,
|
|
- &sensor->temp_critical);
|
|
- if (ret) {
|
|
- dev_err(&pdev->dev,
|
|
- "Not able to read critical_temp: %d\n", ret);
|
|
+ /* Register IRQ into GIC */
|
|
+ ret = stm_register_irq(sensor);
|
|
+ if (ret)
|
|
goto err_tz;
|
|
- }
|
|
-
|
|
- sensor->temp_critical = celsius(sensor->temp_critical);
|
|
-
|
|
- /* Set thresholds for IRQ */
|
|
- sensor->high_temp = sensor->temp_critical;
|
|
-
|
|
- trip = of_thermal_get_trip_points(sensor->th_dev);
|
|
- sensor->num_trips = of_thermal_get_ntrips(sensor->th_dev);
|
|
-
|
|
- /* Find out passive temperature if it exists */
|
|
- for (i = (sensor->num_trips - 1); i >= 0; i--) {
|
|
- if (trip[i].type == THERMAL_TRIP_PASSIVE) {
|
|
- sensor->temp_passive = celsius(trip[i].temperature);
|
|
- /* Update high temperature threshold */
|
|
- sensor->high_temp = sensor->temp_passive;
|
|
- }
|
|
- }
|
|
-
|
|
- /*
|
|
- * Ensure low_temp_enabled flag is disabled.
|
|
- * By disabling low_temp_enabled, low threshold IT will not be
|
|
- * configured neither enabled because it is not needed as high
|
|
- * threshold is set on the lowest temperature trip point after
|
|
- * probe.
|
|
- */
|
|
- sensor->low_temp_enabled = false;
|
|
|
|
- /* Configure and enable HW sensor */
|
|
- ret = stm_thermal_prepare(sensor);
|
|
- if (ret) {
|
|
- dev_err(&pdev->dev,
|
|
- "Not able to enable sensor: %d\n", ret);
|
|
- goto err_tz;
|
|
- }
|
|
+ stm_enable_irq(sensor);
|
|
|
|
/*
|
|
* Thermal_zone doesn't enable hwmon as default,
|
|
@@ -718,8 +565,6 @@ static int stm_thermal_probe(struct platform_device *pdev)
|
|
if (ret)
|
|
goto err_tz;
|
|
|
|
- sensor->mode = THERMAL_DEVICE_ENABLED;
|
|
-
|
|
dev_info(&pdev->dev, "%s: Driver initialized successfully\n",
|
|
__func__);
|
|
|
|
diff --git a/include/dt-bindings/soc/stm32-hdp.h b/include/dt-bindings/soc/stm32-hdp.h
|
|
new file mode 100644
|
|
index 0000000000000..d98665327281b
|
|
--- /dev/null
|
|
+++ b/include/dt-bindings/soc/stm32-hdp.h
|
|
@@ -0,0 +1,108 @@
|
|
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
|
|
+/*
|
|
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
|
|
+ * Author: Roullier Christophe <christophe.roullier@st.com>
|
|
+ * for STMicroelectronics.
|
|
+ */
|
|
+
|
|
+#ifndef _DT_BINDINGS_STM32_HDP_H
|
|
+#define _DT_BINDINGS_STM32_HDP_H
|
|
+
|
|
+#define STM32_HDP(port, value) ((value) << ((port) * 4))
|
|
+
|
|
+/* define HDP Pins number*/
|
|
+#define HDP0_PWR_PWRWAKE_SYS 0
|
|
+#define HDP0_CM4_SLEEPDEEP 1
|
|
+#define HDP0_PWR_STDBY_WKUP 2
|
|
+#define HDP0_PWR_ENCOMP_VDDCORE 3
|
|
+#define HDP0_BSEC_OUT_SEC_NIDEN 4
|
|
+#define HDP0_RCC_CM4_SLEEPDEEP 6
|
|
+#define HDP0_GPU_DBG7 7
|
|
+#define HDP0_DDRCTRL_LP_REQ 8
|
|
+#define HDP0_PWR_DDR_RET_ENABLE_N 9
|
|
+#define HDP0_GPOVAL_0 15
|
|
+
|
|
+#define HDP1_PWR_PWRWAKE_MCU 0
|
|
+#define HDP1_CM4_HALTED 1
|
|
+#define HDP1_CA7_NAXIERRIRQ 2
|
|
+#define HDP1_PWR_OKIN_MR 3
|
|
+#define HDP1_BSEC_OUT_SEC_DBGEN 4
|
|
+#define HDP1_EXTI_SYS_WAKEUP 5
|
|
+#define HDP1_RCC_PWRDS_MPU 6
|
|
+#define HDP1_GPU_DBG6 7
|
|
+#define HDP1_DDRCTRL_DFI_CTRLUPD_REQ 8
|
|
+#define HDP1_DDRCTRL_CACTIVE_DDRC_ASR 9
|
|
+#define HDP1_GPOVAL_1 15
|
|
+
|
|
+#define HDP2_PWR_PWRWAKE_MPU 0
|
|
+#define HDP2_CM4_RXEV 1
|
|
+#define HDP2_CA7_NPMUIRQ1 2
|
|
+#define HDP2_CA7_NFIQOUT1 3
|
|
+#define HDP2_BSEC_IN_RSTCORE_N 4
|
|
+#define HDP2_EXTI_C2_WAKEUP 5
|
|
+#define HDP2_RCC_PWRDS_MCU 6
|
|
+#define HDP2_GPU_DBG5 7
|
|
+#define HDP2_DDRCTRL_DFI_INIT_COMPLETE 8
|
|
+#define HDP2_DDRCTRL_PERF_OP_IS_REFRESH 9
|
|
+#define HDP2_DDRCTRL_GSKP_DFI_LP_REQ 10
|
|
+#define HDP2_GPOVAL_2 15
|
|
+
|
|
+#define HDP3_PWR_SEL_VTH_VDD_CORE 0
|
|
+#define HDP3_CM4_TXEV 1
|
|
+#define HDP3_CA7_NPMUIRQ0 2
|
|
+#define HDP3_CA7_NFIQOUT0 3
|
|
+#define HDP3_BSEC_OUT_SEC_DFTLOCK 4
|
|
+#define HDP3_EXTI_C1_WAKEUP 5
|
|
+#define HDP3_RCC_PWRDS_SYS 6
|
|
+#define HDP3_GPU_DBG4 7
|
|
+#define HDP3_DDRCTRL_STAT_DDRC_REG_SELREF_TYPE0 8
|
|
+#define HDP3_DDRCTRL_CACTIVE_1 9
|
|
+#define HDP3_GPOVAL_3 15
|
|
+
|
|
+#define HDP4_PWR_PDDS 0
|
|
+#define HDP4_CM4_SLEEPING 1
|
|
+#define HDP4_CA7_NRESET1 2
|
|
+#define HDP4_CA7_NIRQOUT1 3
|
|
+#define HDP4_BSEC_OUT_SEC_DFTEN 4
|
|
+#define HDP4_BSEC_OUT_SEC_DBGSWENABLE 5
|
|
+#define HDP4_ETH_OUT_PMT_INTR_O 6
|
|
+#define HDP4_GPU_DBG3 7
|
|
+#define HDP4_DDRCTRL_STAT_DDRC_REG_SELREF_TYPE1 8
|
|
+#define HDP4_DDRCTRL_CACTIVE_0 9
|
|
+#define HDP4_GPOVAL_4 15
|
|
+
|
|
+#define HDP5_CA7_STANDBYWFIL2 0
|
|
+#define HDP5_PWR_VTH_VDDCORE_ACK 1
|
|
+#define HDP5_CA7_NRESET0 2
|
|
+#define HDP5_CA7_NIRQOUT0 3
|
|
+#define HDP5_BSEC_IN_PWROK 4
|
|
+#define HDP5_BSEC_OUT_SEC_DEVICEEN 5
|
|
+#define HDP5_ETH_OUT_LPI_INTR_O 6
|
|
+#define HDP5_GPU_DBG2 7
|
|
+#define HDP5_DDRCTRL_CACTIVE_DDRC 8
|
|
+#define HDP5_DDRCTRL_WR_CREDIT_CNT 9
|
|
+#define HDP5_GPOVAL_5 15
|
|
+
|
|
+#define HDP6_CA7_STANDBYWFI1 0
|
|
+#define HDP6_CA7_STANDBYWFE1 1
|
|
+#define HDP6_CA7_EVENT0 2
|
|
+#define HDP6_CA7_DBGACK1 3
|
|
+#define HDP6_BSEC_OUT_SEC_SPNIDEN 5
|
|
+#define HDP6_ETH_OUT_MAC_SPEED_O1 6
|
|
+#define HDP6_GPU_DBG1 7
|
|
+#define HDP6_DDRCTRL_CSYSACK_DDRC 8
|
|
+#define HDP6_DDRCTRL_LPR_CREDIT_CNT 9
|
|
+#define HDP6_GPOVAL_6 15
|
|
+
|
|
+#define HDP7_CA7_STANDBYWFI0 0
|
|
+#define HDP7_CA7_STANDBYWFE0 1
|
|
+#define HDP7_CA7_DBGACK0 3
|
|
+#define HDP7_BSEC_OUT_FUSE_OK 4
|
|
+#define HDP7_BSEC_OUT_SEC_SPIDEN 5
|
|
+#define HDP7_ETH_OUT_MAC_SPEED_O0 6
|
|
+#define HDP7_GPU_DBG0 7
|
|
+#define HDP7_DDRCTRL_CSYSREQ_DDRC 8
|
|
+#define HDP7_DDRCTRL_HPR_CREDIT_CNT 9
|
|
+#define HDP7_GPOVAL_7 15
|
|
+
|
|
+#endif /* _DT_BINDINGS_STM32_HDP_H */
|
|
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
|
|
index f6a7bcd131977..c3dd47f14c1ac 100644
|
|
--- a/include/media/v4l2-fwnode.h
|
|
+++ b/include/media/v4l2-fwnode.h
|
|
@@ -50,11 +50,13 @@ struct v4l2_fwnode_bus_mipi_csi2 {
|
|
* @flags: media bus (V4L2_MBUS_*) flags
|
|
* @bus_width: bus width in bits
|
|
* @data_shift: data shift in bits
|
|
+ * @max_pclk_frequency: maximum pixel clock in hertz
|
|
*/
|
|
struct v4l2_fwnode_bus_parallel {
|
|
unsigned int flags;
|
|
unsigned char bus_width;
|
|
unsigned char data_shift;
|
|
+ unsigned int pclk_max_frequency;
|
|
};
|
|
|
|
/**
|
|
--
|
|
2.17.1
|
|
|