Merge commit '6d08fe13929b0093002b716aadcf33534def212c'

* commit '6d08fe13929b0093002b716aadcf33534def212c': (21 commits)
  arm64: dts: rockchip: Add fp9931 pmic config for rk3576-ebook-v10
  arm64: dts: rockchip: Add sy7636a power good pin for rk3576-ebook-v10
  PCI: rockchip: dw: Add dwc pmu support for rockchip
  perf/dwc_pcie: Add support for Rockchip vendor devices
  BACKPORT: drivers/perf: add DesignWare PCIe PMU driver
  UPSTREAM: PCI: Add Alibaba Vendor ID to linux/pci_ids.h
  UPSTREAM: PCI: Move pci_clear_and_set_dword() helper to PCI header
  arm64: dts: rockchip: rk3576-vehicle-evb: fix evb v20 pwm setting
  phy: rockchip-snps-pcie3: Check the mplla_state/mpllb_state
  PCI: rockchip: dw: Add retrain link support
  PCI: dw: rockchip: Fully reset controller in initial process
  arm64: dts: rockchip: rk3576-test5-v10 modify pwdn-gpio of camera
  ARM: dts: rockchip: rk3506g-evb1: enable rockchip,vbus-always-on for bt1120/bt656/mcu/rgb display board
  ARM: dts: rockchip: rk3506g-iotest: add vbus-always-on for u2phy
  net: wireless: rockchip_wlan: bcmdhd: Calling ROCKCHIP_PCIE_PM_CTRL_RESET when wifi on
  PCI: rockchip: dw: Optimize the pm process L1SS workflow
  spi: rockchip: The cs-high function of the controller is only valid for function io
  ARM: dts: rockchip: rk3506g-evb1: update cma size for bt1120/bt656/mcu/rgb display boards
  thermal: rockchip: Add trim temperature for rk3506
  ARM: dts: rockchip: rk3506: Add trim configure for tsadc
  ...

Change-Id: I186d3fd8bcfb888c3363ba635b8baffc99434f16
This commit is contained in:
Tao Huang
2024-09-25 19:09:33 +08:00
31 changed files with 1150 additions and 454 deletions

View File

@@ -1244,6 +1244,13 @@
log_leakage: log-leakage@1f {
reg = <0x1f 0x1>;
};
cpu_tsadc_trim_l: cpu-tsadc-trim-l@20 {
reg = <0x20 0x1>;
};
cpu_tsadc_trim_h: cpu-tsadc-trim-h@21 {
reg = <0x21 0x1>;
bits = <0 2>;
};
};
audio_codec: audio-codec@ff4f8000 {
@@ -1352,6 +1359,8 @@
rockchip,hw-tshut-temp = <120000>;
rockchip,hw-tshut-mode = <0>; /* tshut mode 0:CRU 1:GPIO */
rockchip,hw-tshut-polarity = <0>; /* tshut polarity 0:LOW 1:HIGH */
nvmem-cells = <&cpu_tsadc_trim_l>, <&cpu_tsadc_trim_h>;
nvmem-cell-names = "trim_l", "trim_h";
status = "disabled";
};

View File

@@ -14,6 +14,10 @@
compatible = "rockchip,rk3506g-evb1-v10-mcu-k350c4516t", "rockchip,rk3506";
};
&cma {
size = <0x1600000>;
};
&rgb {
status = "okay";
rockchip,data-sync-bypass;
@@ -214,6 +218,10 @@
status = "okay";
};
&u2phy_otg0 {
rockchip,vbus-always-on;
};
&vop {
mcu-timing {
mcu-pix-total = <6>;

View File

@@ -52,6 +52,10 @@
};
};
&cma {
size = <0x1600000>;
};
&rgb {
status = "okay";
pinctrl-names = "default";
@@ -78,3 +82,7 @@
&route_rgb {
status = "okay";
};
&u2phy_otg0 {
rockchip,vbus-always-on;
};

View File

@@ -14,6 +14,10 @@
compatible = "rockchip,rk3506g-evb1-v10-sii9022-bt1120-to-hdmi", "rockchip,rk3506";
};
&cma {
size = <0x1600000>;
};
&i2c2 {
clock-frequency = <400000>;
pinctrl-names = "default";
@@ -89,3 +93,7 @@
&route_rgb {
status = "okay";
};
&u2phy_otg0 {
rockchip,vbus-always-on;
};

View File

@@ -14,6 +14,10 @@
compatible = "rockchip,rk3506g-evb1-v10-sii9022-rgb2hdmi", "rockchip,rk3506";
};
&cma {
size = <0x1600000>;
};
&i2c2 {
clock-frequency = <400000>;
pinctrl-0 = <&rm_io4_i2c2_scl &rm_io5_i2c2_sda>;
@@ -80,3 +84,7 @@
&route_rgb {
status = "okay";
};
&u2phy_otg0 {
rockchip,vbus-always-on;
};

View File

@@ -203,6 +203,7 @@
};
&u2phy_otg0 {
rockchip,vbus-always-on;
status = "okay";
};

View File

@@ -536,6 +536,7 @@
pinctrl-0 = <&sy7636a_default>;
enable-gpios = <&gpio3 RK_PA4 GPIO_ACTIVE_HIGH>, <&gpio2 RK_PD1 GPIO_ACTIVE_HIGH>;
pgood-gpio = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>;
thermal-zone = "ebcpmic-thermal";
#thermal-sensor-cells = <0>;
@@ -799,7 +800,8 @@
sy7636a_default: sy7636a_default {
rockchip,pins =
<3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>,
<2 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>;
<2 RK_PD1 RK_FUNC_GPIO &pcfg_pull_up>,
<3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
};
};

View File

@@ -15,3 +15,19 @@
model = "Rockchip RK3576 TEST5 V10 Board";
compatible = "rockchip,rk3576-test5-v10", "rockchip,rk3576";
};
&imx464_0 {
pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>;
};
&imx464_1 {
pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>;
};
&os04a10 {
pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>;
};
&sc4336 {
pwdn-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_HIGH>;
};

View File

@@ -1640,7 +1640,7 @@
/* dsi->serdes->lvds_panel */
&pwm1_6ch_1 {
status = "okay";
pinctrl-0 = <&pwm1m0_ch1>;
pinctrl-0 = <&pwm1m2_ch1>;
};
/* dp->serdes->lvds_panel */

View File

@@ -11,8 +11,6 @@
#include <linux/types.h>
/* PCIe device related definition. */
#define PCI_VENDOR_ID_ALIBABA 0x1ded
#define ERDMA_PCI_WIDTH 64
#define ERDMA_FUNC_BAR 0
#define ERDMA_MISX_BAR 2

View File

@@ -365,6 +365,108 @@ void rkisp_config_dmatx_valid_buf(struct rkisp_device *dev)
}
}
void rkisp_stream_vir_cpy_image(struct work_struct *work)
{
struct rkisp_vir_cpy *cpy = container_of(work, struct rkisp_vir_cpy, work);
struct rkisp_stream *vir = cpy->stream;
struct rkisp_buffer *src_buf = NULL;
struct vb2_buffer *src_vb = NULL;
struct rkisp_device *isp_dev = vir->ispdev;
const struct vb2_mem_ops *g_ops = isp_dev->hw_dev->mem_ops;
void *src = NULL, *dst = NULL, *mem = NULL;
u32 payload_size = 0;
unsigned long lock_flags = 0;
u32 i;
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s enter\n", __func__);
vir->streaming = true;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
while (src_buf || vir->streaming) {
if (vir->stopping || !vir->streaming)
goto end;
if (!src_buf)
wait_for_completion(&cpy->cmpl);
vir->frame_end = false;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!src_buf && !list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue, struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) {
vir->curr_buf = list_first_entry(&vir->buf_queue,
struct rkisp_buffer, queue);
list_del(&vir->curr_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
if (!vir->curr_buf || !src_buf)
goto end;
src_vb = &src_buf->vb.vb2_buf;
for (i = 0; i < vir->out_isp_fmt.mplanes; i++) {
payload_size = vir->out_fmt.plane_fmt[i].sizeimage;
dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i);
mem = src_vb->planes[i].mem_priv;
src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i);
if (!src || !dst)
break;
/* sync cache */
if (mem)
g_ops->finish(mem);
vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size);
memcpy(dst, src, payload_size);
}
vir->curr_buf->vb.sequence = src_buf->vb.sequence;
vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp;
vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
vir->curr_buf = NULL;
end:
if (src_buf)
vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
src_buf = NULL;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
} else if (vir->stopping) {
vir->streaming = false;
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
}
vir->frame_end = true;
if (vir->stopping) {
vir->stopping = false;
vir->streaming = false;
wake_up(&vir->done);
}
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s exit\n", __func__);
}
/* Get xsubs and ysubs for fourcc formats
*
* @xsubs: horizontal color samples in a 4*4 matrix, for yuv
@@ -746,6 +848,11 @@ static int rkisp_set_fmt(struct rkisp_stream *stream,
t = &dev->cap_dev.stream[i];
if (t->out_isp_fmt.fmt_type != FMT_YUV || !t->streaming)
continue;
/* isp v32 v33 mp wrap can't use for iqtool */
if (i == RKISP_STREAM_MP &&
(dev->isp_ver == ISP_V32 || dev->isp_ver == ISP_V33) &&
dev->cap_dev.wrap_line)
continue;
if (t->out_fmt.plane_fmt[0].sizeimage > imagsize) {
imagsize = t->out_fmt.plane_fmt[0].sizeimage;
*pixm = t->out_fmt;
@@ -1252,6 +1359,13 @@ static int rkisp_set_iqtool_connect_id(struct rkisp_stream *stream, int stream_i
goto err;
}
if ((dev->isp_ver == ISP_V32 || dev->isp_ver == ISP_V33) &&
(stream_id == RKISP_STREAM_MP) &&
dev->cap_dev.wrap_line) {
v4l2_err(&dev->v4l2_dev, "isp v32 v33 mp wrap can't use for iqtool");
goto err;
}
stream->conn_id = stream_id;
return 0;
err:

View File

@@ -345,6 +345,7 @@ extern struct stream_config rkisp_mp_stream_config;
extern struct stream_config rkisp_sp_stream_config;
extern struct rockit_isp_ops rockit_isp_ops;
void rkisp_stream_vir_cpy_image(struct work_struct *work);
void rkisp_stream_buf_done_early(struct rkisp_device *dev);
void rkisp_stream_buf_done(struct rkisp_stream *stream,
struct rkisp_buffer *buf);

View File

@@ -1404,98 +1404,6 @@ static void rkisp_stream_stop(struct rkisp_stream *stream)
stream->interlaced = false;
}
static void vir_cpy_image(struct work_struct *work)
{
struct rkisp_vir_cpy *cpy =
container_of(work, struct rkisp_vir_cpy, work);
struct rkisp_stream *vir = cpy->stream;
struct rkisp_buffer *src_buf = NULL;
unsigned long lock_flags = 0;
u32 i;
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s enter\n", __func__);
vir->streaming = true;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
while (src_buf || vir->streaming) {
if (vir->stopping || !vir->streaming)
goto end;
if (!src_buf)
wait_for_completion(&cpy->cmpl);
vir->frame_end = false;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!src_buf && !list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) {
vir->curr_buf = list_first_entry(&vir->buf_queue,
struct rkisp_buffer, queue);
list_del(&vir->curr_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
if (!vir->curr_buf || !src_buf)
goto end;
for (i = 0; i < vir->out_isp_fmt.mplanes; i++) {
u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage;
void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i);
void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i);
if (!src || !dst)
break;
vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size);
memcpy(dst, src, payload_size);
}
vir->curr_buf->vb.sequence = src_buf->vb.sequence;
vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp;
vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
vir->curr_buf = NULL;
end:
if (src_buf)
vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
src_buf = NULL;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
} else if (vir->stopping) {
vir->streaming = false;
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
}
vir->frame_end = true;
if (vir->stopping) {
vir->stopping = false;
vir->streaming = false;
wake_up(&vir->done);
}
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s exit\n", __func__);
}
/*
* Most of registers inside rockchip isp1 have shadow register since
* they must be not changed during processing a frame.
@@ -1793,7 +1701,7 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count)
struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id];
if (t->streaming) {
INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image);
INIT_WORK(&dev->cap_dev.vir_cpy.work, rkisp_stream_vir_cpy_image);
init_completion(&dev->cap_dev.vir_cpy.cmpl);
INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue);
dev->cap_dev.vir_cpy.stream = stream;

View File

@@ -1305,97 +1305,6 @@ end:
mutex_unlock(&dev->hw_dev->dev_lock);
}
static void vir_cpy_image(struct work_struct *work)
{
struct rkisp_vir_cpy *cpy =
container_of(work, struct rkisp_vir_cpy, work);
struct rkisp_stream *vir = cpy->stream;
struct rkisp_buffer *src_buf = NULL;
unsigned long lock_flags = 0;
u32 i;
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s enter\n", __func__);
vir->streaming = true;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
while (src_buf || vir->streaming) {
if (vir->stopping || !vir->streaming)
goto end;
if (!src_buf)
wait_for_completion(&cpy->cmpl);
vir->frame_end = false;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!src_buf && !list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) {
vir->curr_buf = list_first_entry(&vir->buf_queue,
struct rkisp_buffer, queue);
list_del(&vir->curr_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
if (!vir->curr_buf || !src_buf)
goto end;
for (i = 0; i < vir->out_isp_fmt.mplanes; i++) {
u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage;
void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i);
void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i);
if (!src || !dst)
break;
vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size);
memcpy(dst, src, payload_size);
}
vir->curr_buf->vb.sequence = src_buf->vb.sequence;
vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp;
vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
vir->curr_buf = NULL;
end:
if (src_buf)
vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
src_buf = NULL;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
} else if (vir->stopping) {
vir->streaming = false;
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
}
vir->frame_end = true;
if (vir->stopping) {
vir->stopping = false;
vir->streaming = false;
wake_up(&vir->done);
}
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s exit\n", __func__);
}
static int rkisp_stream_start(struct rkisp_stream *stream)
{
struct v4l2_device *v4l2_dev = &stream->ispdev->v4l2_dev;
@@ -1452,7 +1361,7 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count)
struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id];
if (t->streaming) {
INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image);
INIT_WORK(&dev->cap_dev.vir_cpy.work, rkisp_stream_vir_cpy_image);
init_completion(&dev->cap_dev.vir_cpy.cmpl);
INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue);
dev->cap_dev.vir_cpy.stream = stream;

View File

@@ -1943,97 +1943,6 @@ end:
}
}
static void vir_cpy_image(struct work_struct *work)
{
struct rkisp_vir_cpy *cpy =
container_of(work, struct rkisp_vir_cpy, work);
struct rkisp_stream *vir = cpy->stream;
struct rkisp_buffer *src_buf = NULL;
unsigned long lock_flags = 0;
u32 i;
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s enter\n", __func__);
vir->streaming = true;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
while (src_buf || vir->streaming) {
if (vir->stopping || !vir->streaming)
goto end;
if (!src_buf)
wait_for_completion(&cpy->cmpl);
vir->frame_end = false;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!src_buf && !list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) {
vir->curr_buf = list_first_entry(&vir->buf_queue,
struct rkisp_buffer, queue);
list_del(&vir->curr_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
if (!vir->curr_buf || !src_buf)
goto end;
for (i = 0; i < vir->out_isp_fmt.mplanes; i++) {
u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage;
void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i);
void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i);
if (!src || !dst)
break;
vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size);
memcpy(dst, src, payload_size);
}
vir->curr_buf->vb.sequence = src_buf->vb.sequence;
vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp;
vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
vir->curr_buf = NULL;
end:
if (src_buf)
vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
src_buf = NULL;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
} else if (vir->stopping) {
vir->streaming = false;
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
}
vir->frame_end = true;
if (vir->stopping) {
vir->stopping = false;
vir->streaming = false;
wake_up(&vir->done);
}
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s exit\n", __func__);
}
static int rkisp_stream_start(struct rkisp_stream *stream)
{
struct rkisp_device *dev = stream->ispdev;
@@ -2096,7 +2005,7 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count)
struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id];
if (t->streaming) {
INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image);
INIT_WORK(&dev->cap_dev.vir_cpy.work, rkisp_stream_vir_cpy_image);
init_completion(&dev->cap_dev.vir_cpy.cmpl);
INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue);
dev->cap_dev.vir_cpy.stream = stream;
@@ -2342,11 +2251,14 @@ int rkisp_register_stream_v32(struct rkisp_device *dev)
ret = rkisp_stream_init(dev, RKISP_STREAM_SP);
if (ret < 0)
goto err_free_mp;
ret = rkisp_stream_init(dev, RKISP_STREAM_VIR);
if (ret < 0)
goto err_free_sp;
if (dev->isp_ver == ISP_V32) {
ret = rkisp_stream_init(dev, RKISP_STREAM_BP);
if (ret < 0)
goto err_free_sp;
goto err_free_vir;
ret = rkisp_stream_init(dev, RKISP_STREAM_MPDS);
if (ret < 0)
goto err_free_bp;
@@ -2370,6 +2282,8 @@ err_free_mpds:
rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_MPDS]);
err_free_bp:
rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_BP]);
err_free_vir:
rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_VIR]);
err_free_sp:
rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_SP]);
err_free_mp:
@@ -2387,6 +2301,8 @@ void rkisp_unregister_stream_v32(struct rkisp_device *dev)
rkisp_unregister_stream_vdev(stream);
stream = &cap_dev->stream[RKISP_STREAM_SP];
rkisp_unregister_stream_vdev(stream);
stream = &cap_dev->stream[RKISP_STREAM_VIR];
rkisp_unregister_stream_vdev(stream);
if (dev->isp_ver == ISP_V32) {
stream = &cap_dev->stream[RKISP_STREAM_BP];
rkisp_unregister_stream_vdev(stream);

View File

@@ -1387,97 +1387,6 @@ end:
mutex_unlock(&dev->hw_dev->dev_lock);
}
static void vir_cpy_image(struct work_struct *work)
{
struct rkisp_vir_cpy *cpy =
container_of(work, struct rkisp_vir_cpy, work);
struct rkisp_stream *vir = cpy->stream;
struct rkisp_buffer *src_buf = NULL;
unsigned long lock_flags = 0;
u32 i;
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s enter\n", __func__);
vir->streaming = true;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
while (src_buf || vir->streaming) {
if (vir->stopping || !vir->streaming)
goto end;
if (!src_buf)
wait_for_completion(&cpy->cmpl);
vir->frame_end = false;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!src_buf && !list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
}
if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) {
vir->curr_buf = list_first_entry(&vir->buf_queue,
struct rkisp_buffer, queue);
list_del(&vir->curr_buf->queue);
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
if (!vir->curr_buf || !src_buf)
goto end;
for (i = 0; i < vir->out_isp_fmt.mplanes; i++) {
u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage;
void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i);
void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i);
if (!src || !dst)
break;
vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size);
memcpy(dst, src, payload_size);
}
vir->curr_buf->vb.sequence = src_buf->vb.sequence;
vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp;
vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
vir->curr_buf = NULL;
end:
if (src_buf)
vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
src_buf = NULL;
spin_lock_irqsave(&vir->vbq_lock, lock_flags);
if (!list_empty(&cpy->queue)) {
src_buf = list_first_entry(&cpy->queue,
struct rkisp_buffer, queue);
list_del(&src_buf->queue);
} else if (vir->stopping) {
vir->streaming = false;
}
spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
}
vir->frame_end = true;
if (vir->stopping) {
vir->stopping = false;
vir->streaming = false;
wake_up(&vir->done);
}
v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
"%s exit\n", __func__);
}
static int rkisp_stream_start(struct rkisp_stream *stream)
{
struct rkisp_device *dev = stream->ispdev;
@@ -1536,7 +1445,7 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count)
struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id];
if (t->streaming) {
INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image);
INIT_WORK(&dev->cap_dev.vir_cpy.work, rkisp_stream_vir_cpy_image);
init_completion(&dev->cap_dev.vir_cpy.cmpl);
INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue);
dev->cap_dev.vir_cpy.stream = stream;

View File

@@ -108,6 +108,10 @@
#endif /* DHD_CONTROL_PCIE_CPUCORE_WIFI_TURNON */
#include <dhd_linux_pktdump.h>
#ifdef CONFIG_ARCH_ROCKCHIP
#include <linux/aspm_ext.h>
#endif
#define EXTENDED_PCIE_DEBUG_DUMP 1 /* Enable Extended pcie registers dump */
#define MEMBLOCK 2048 /* Block size used for downloading of dongle image */
@@ -7920,6 +7924,17 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag)
DHD_ERROR(("%s: == Power ON ==\n", __FUNCTION__));
/* PCIe RC Turn on */
do {
#ifdef CONFIG_ARCH_ROCKCHIP
if (bus->rc_dev) {
bcmerror = rockchip_dw_pcie_pm_ctrl_for_user(bus->rc_dev, ROCKCHIP_PCIE_PM_CTRL_RESET);
if (bcmerror) {
DHD_ERROR(("%s Failed to bring up PCIe link\n", __FUNCTION__));
OSL_SLEEP(10);
continue;
}
}
#endif /* CONFIG_ARCH_ROCKCHIP */
bcmerror = dhdpcie_bus_start_host_dev(bus);
if (!bcmerror) {
DHD_ERROR_MEM(("%s: dhdpcie_bus_start_host_dev OK\n",

View File

@@ -2403,11 +2403,6 @@ dhdpcie_start_host_dev(dhd_bus_t *bus)
ret = msm_pcie_pm_control(MSM_PCIE_RESUME, bus->dev->bus->number,
bus->dev, NULL, 0);
#endif /* CONFIG_ARCH_MSM */
#ifdef CONFIG_ARCH_ROCKCHIP
if (bus->rc_dev)
ret = rockchip_dw_pcie_pm_ctrl_for_user(bus->rc_dev, ROCKCHIP_PCIE_PM_CTRL_RESET);
#endif /* CONFIG_ARCH_ROCKCHIP */
if (ret) {
DHD_ERROR(("%s Failed to bring up PCIe link\n", __FUNCTION__));
goto done;

View File

@@ -580,3 +580,15 @@ int pci_write_config_dword(const struct pci_dev *dev, int where,
return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val);
}
EXPORT_SYMBOL(pci_write_config_dword);
void pci_clear_and_set_config_dword(const struct pci_dev *dev, int pos,
u32 clear, u32 set)
{
u32 val;
pci_read_config_dword(dev, pos, &val);
val &= ~clear;
val |= set;
pci_write_config_dword(dev, pos, val);
}
EXPORT_SYMBOL(pci_clear_and_set_config_dword);

View File

@@ -92,6 +92,7 @@ config PCIE_DW_ROCKCHIP
config PCIE_RK_THREADED_INIT
bool "Threaded initialize Rockchip DW based PCIe controller"
depends on PCIE_DW_ROCKCHIP
depends on !DWC_PCIE_PMU
default y
help
Enables threaded initialize Rockchip DW based PCIe controller.

View File

@@ -143,6 +143,7 @@ struct rk_pcie {
raw_spinlock_t intx_lock;
u16 aspm;
u32 l1ss_ctl1;
u32 l1ss_ctl2;
struct dentry *debugfs;
u32 msi_vector_num;
struct workqueue_struct *hot_rst_wq;
@@ -303,6 +304,34 @@ static int rk_pcie_disable_power(struct rk_pcie *rk_pcie)
return ret;
}
static void rk_pcie_retrain(struct dw_pcie *pci)
{
u32 pcie_cap_off;
u16 lnk_stat, lnk_ctl;
int ret;
/*
* Set retrain bit if current speed is 2.5 GB/s,
* but the PCIe root port support is > 2.5 GB/s.
*/
if (pci->link_gen < 2)
return;
dev_info(pci->dev, "Retrain link..\n");
pcie_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
lnk_stat = dw_pcie_readw_dbi(pci, pcie_cap_off + PCI_EXP_LNKSTA);
if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
lnk_ctl = dw_pcie_readw_dbi(pci, pcie_cap_off + PCI_EXP_LNKCTL);
lnk_ctl |= PCI_EXP_LNKCTL_RL;
dw_pcie_writew_dbi(pci, pcie_cap_off + PCI_EXP_LNKCTL, lnk_ctl);
ret = readw_poll_timeout(pci->dbi_base + pcie_cap_off + PCI_EXP_LNKSTA,
lnk_stat, ((lnk_stat & PCI_EXP_LNKSTA_LT) == 0), 1000, HZ);
if (ret)
dev_err(pci->dev, "Retrain link timeout\n");
}
}
static int rk_pcie_establish_link(struct dw_pcie *pci)
{
int retries, power;
@@ -1312,6 +1341,8 @@ static int rk_pcie_really_probe(void *p)
if (ret)
goto release_driver;
reset_control_assert(rk_pcie->rsts);
udelay(10);
reset_control_deassert(rk_pcie->rsts);
ret = clk_bulk_prepare_enable(rk_pcie->clk_cnt, rk_pcie->clks);
@@ -1434,46 +1465,69 @@ static void rk_pcie_downstream_dev_to_d0(struct rk_pcie *rk_pcie, bool enable)
}
/* Save and restore root bus ASPM */
if (enable) {
if (rk_pcie->l1ss_ctl1)
dw_pcie_writel_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL1, rk_pcie->l1ss_ctl1);
/* rk_pcie->aspm woule be saved in advance when enable is false */
dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, rk_pcie->aspm);
} else {
if (!enable) {
val = dw_pcie_readl_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL1);
if (val & PCI_L1SS_CTL1_L1SS_MASK)
if (val & PCI_L1SS_CTL1_L1SS_MASK) {
rk_pcie->l1ss_ctl1 = val;
else
rk_pcie->l1ss_ctl2 = dw_pcie_readl_dbi(rk_pcie->pci,
bridge->l1ss + PCI_L1SS_CTL2);
} else {
rk_pcie->l1ss_ctl1 = 0;
rk_pcie->l1ss_ctl2 = 0;
}
val = dw_pcie_readl_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL);
rk_pcie->aspm = val & PCI_EXP_LNKCTL_ASPMC;
val &= ~(PCI_EXP_LNKCAP_ASPM_L1 | PCI_EXP_LNKCAP_ASPM_L0S);
dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, val);
}
if (enable) {
if (rk_pcie->l1ss_ctl1) {
dw_pcie_writel_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL2,
rk_pcie->l1ss_ctl2);
dw_pcie_writel_dbi(rk_pcie->pci, bridge->l1ss + PCI_L1SS_CTL1,
rk_pcie->l1ss_ctl1);
if (bridge->ltr_path)
pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2, 0x0400);
}
}
if (enable) {
list_for_each_entry(pdev, &root_bus->devices, bus_list) {
if (PCI_SLOT(pdev->devfn) == 0) {
if (rk_pcie->l1ss_ctl1) {
pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL2,
rk_pcie->l1ss_ctl2);
pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1,
rk_pcie->l1ss_ctl1);
}
}
}
}
if (enable)
dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, rk_pcie->aspm);
list_for_each_entry(pdev, &root_bus->devices, bus_list) {
if (PCI_SLOT(pdev->devfn) == 0) {
if (pci_set_power_state(pdev, PCI_D0))
dev_err(rk_pcie->pci->dev,
"Failed to transition %s to D3hot state\n",
"Failed to transition %s to D0 state\n",
dev_name(&pdev->dev));
if (enable) {
if (rk_pcie->l1ss_ctl1) {
pci_read_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, &val);
val &= ~PCI_L1SS_CTL1_L1SS_MASK;
val |= (rk_pcie->l1ss_ctl1 & PCI_L1SS_CTL1_L1SS_MASK);
pci_write_config_dword(pdev, pdev->l1ss + PCI_L1SS_CTL1, val);
}
if (enable)
pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
PCI_EXP_LNKCTL_ASPMC, rk_pcie->aspm);
} else {
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
}
PCI_EXP_LNKCTL_ASPMC,
rk_pcie->aspm);
else
pci_disable_link_state(pdev,
PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
}
}
if (!enable) {
val = dw_pcie_readl_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL);
val &= ~(PCI_EXP_LNKCTL_ASPM_L1 | PCI_EXP_LNKCTL_ASPM_L0S);
dw_pcie_writel_dbi(rk_pcie->pci, bridge->pcie_cap + PCI_EXP_LNKCTL, val);
}
}
#endif
@@ -1658,6 +1712,9 @@ int rockchip_dw_pcie_pm_ctrl_for_user(struct pci_dev *dev, enum rockchip_pcie_pm
rockchip_dw_pcie_suspend(rk_pcie->pci->dev);
rockchip_dw_pcie_resume(rk_pcie->pci->dev);
break;
case ROCKCHIP_PCIE_PM_RETRAIN_LINK:
rk_pcie_retrain(pci);
break;
default:
dev_err(rk_pcie->pci->dev, "%s flag=%d invalid\n", __func__, flag);
return -EINVAL;

View File

@@ -474,17 +474,6 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
}
}
static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
u32 clear, u32 set)
{
u32 val;
pci_read_config_dword(pdev, pos, &val);
val &= ~clear;
val |= set;
pci_write_config_dword(pdev, pos, val);
}
/* Calculate L1.2 PM substate timing parameters */
static void aspm_calc_l1ss_info(struct pcie_link_state *link,
u32 parent_l1ss_cap, u32 child_l1ss_cap)
@@ -548,10 +537,12 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
cl1_2_enables = cctl1 & PCI_L1SS_CTL1_L1_2_MASK;
if (pl1_2_enables || cl1_2_enables) {
pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1_2_MASK, 0);
pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1_2_MASK, 0);
pci_clear_and_set_config_dword(child,
child->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1_2_MASK, 0);
pci_clear_and_set_config_dword(parent,
parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1_2_MASK, 0);
}
/* Program T_POWER_ON times in both ports */
@@ -559,22 +550,26 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
pci_write_config_dword(child, child->l1ss + PCI_L1SS_CTL2, ctl2);
/* Program Common_Mode_Restore_Time in upstream device */
pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1);
pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1);
/* Program LTR_L1.2_THRESHOLD time in both ports */
pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1);
pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1);
pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
PCI_L1SS_CTL1_LTR_L12_TH_SCALE,
ctl1);
pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
PCI_L1SS_CTL1_LTR_L12_TH_SCALE,
ctl1);
if (pl1_2_enables || cl1_2_enables) {
pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 0,
pl1_2_enables);
pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 0,
cl1_2_enables);
pci_clear_and_set_config_dword(parent,
parent->l1ss + PCI_L1SS_CTL1, 0,
pl1_2_enables);
pci_clear_and_set_config_dword(child,
child->l1ss + PCI_L1SS_CTL1, 0,
cl1_2_enables);
}
}
@@ -734,10 +729,10 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
*/
/* Disable all L1 substates */
pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1SS_MASK, 0);
pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1SS_MASK, 0);
pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1SS_MASK, 0);
pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1SS_MASK, 0);
/*
* If needed, disable L1, and it gets enabled later
* in pcie_config_aspm_link().
@@ -760,10 +755,10 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
val |= PCI_L1SS_CTL1_PCIPM_L1_2;
/* Enable what we need to enable */
pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1SS_MASK, val);
pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1SS_MASK, val);
pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1SS_MASK, val);
pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1,
PCI_L1SS_CTL1_L1SS_MASK, val);
}
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)

View File

@@ -209,4 +209,11 @@ config MARVELL_CN10K_DDR_PMU
Enable perf support for Marvell DDR Performance monitoring
event on CN10K platform.
config DWC_PCIE_PMU
tristate "Synopsys DesignWare PCIe PMU"
depends on PCI
help
Enable perf support for Synopsys DesignWare PCIe PMU Performance
monitoring event on platform including the Alibaba Yitian 710.
endmenu

View File

@@ -22,3 +22,4 @@ obj-$(CONFIG_MARVELL_CN10K_TAD_PMU) += marvell_cn10k_tad_pmu.o
obj-$(CONFIG_MARVELL_CN10K_DDR_PMU) += marvell_cn10k_ddr_pmu.o
obj-$(CONFIG_APPLE_M1_CPU_PMU) += apple_m1_cpu_pmu.o
obj-$(CONFIG_ALIBABA_UNCORE_DRW_PMU) += alibaba_uncore_drw_pmu.o
obj-$(CONFIG_DWC_PCIE_PMU) += dwc_pcie_pmu.o

792
drivers/perf/dwc_pcie_pmu.c Normal file
View File

@@ -0,0 +1,792 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Synopsys DesignWare PCIe PMU driver
*
* Copyright (C) 2021-2023 Alibaba Inc.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cpuhotplug.h>
#include <linux/cpumask.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/perf_event.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/smp.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#define DWC_PCIE_VSEC_RAS_DES_ID 0x02
#define DWC_PCIE_EVENT_CNT_CTL 0x8
/*
* Event Counter Data Select includes two parts:
* - 27-24: Group number(4-bit: 0..0x7)
* - 23-16: Event number(8-bit: 0..0x13) within the Group
*
* Put them together as in TRM.
*/
#define DWC_PCIE_CNT_EVENT_SEL GENMASK(27, 16)
#define DWC_PCIE_CNT_LANE_SEL GENMASK(11, 8)
#define DWC_PCIE_CNT_STATUS BIT(7)
#define DWC_PCIE_CNT_ENABLE GENMASK(4, 2)
#define DWC_PCIE_PER_EVENT_OFF 0x1
#define DWC_PCIE_PER_EVENT_ON 0x3
#define DWC_PCIE_EVENT_CLEAR GENMASK(1, 0)
#define DWC_PCIE_EVENT_PER_CLEAR 0x1
#define DWC_PCIE_EVENT_CNT_DATA 0xC
#define DWC_PCIE_TIME_BASED_ANAL_CTL 0x10
#define DWC_PCIE_TIME_BASED_REPORT_SEL GENMASK(31, 24)
#define DWC_PCIE_TIME_BASED_DURATION_SEL GENMASK(15, 8)
#define DWC_PCIE_DURATION_MANUAL_CTL 0x0
#define DWC_PCIE_DURATION_1MS 0x1
#define DWC_PCIE_DURATION_10MS 0x2
#define DWC_PCIE_DURATION_100MS 0x3
#define DWC_PCIE_DURATION_1S 0x4
#define DWC_PCIE_DURATION_2S 0x5
#define DWC_PCIE_DURATION_4S 0x6
#define DWC_PCIE_DURATION_4US 0xFF
#define DWC_PCIE_TIME_BASED_TIMER_START BIT(0)
#define DWC_PCIE_TIME_BASED_CNT_ENABLE 0x1
#define DWC_PCIE_TIME_BASED_ANAL_DATA_REG_LOW 0x14
#define DWC_PCIE_TIME_BASED_ANAL_DATA_REG_HIGH 0x18
/* Event attributes */
#define DWC_PCIE_CONFIG_EVENTID GENMASK(15, 0)
#define DWC_PCIE_CONFIG_TYPE GENMASK(19, 16)
#define DWC_PCIE_CONFIG_LANE GENMASK(27, 20)
#define DWC_PCIE_EVENT_ID(event) FIELD_GET(DWC_PCIE_CONFIG_EVENTID, (event)->attr.config)
#define DWC_PCIE_EVENT_TYPE(event) FIELD_GET(DWC_PCIE_CONFIG_TYPE, (event)->attr.config)
#define DWC_PCIE_EVENT_LANE(event) FIELD_GET(DWC_PCIE_CONFIG_LANE, (event)->attr.config)
enum dwc_pcie_event_type {
DWC_PCIE_TIME_BASE_EVENT,
DWC_PCIE_LANE_EVENT,
DWC_PCIE_EVENT_TYPE_MAX,
};
#define DWC_PCIE_LANE_EVENT_MAX_PERIOD GENMASK_ULL(31, 0)
#define DWC_PCIE_MAX_PERIOD GENMASK_ULL(63, 0)
struct dwc_pcie_pmu {
struct pmu pmu;
struct pci_dev *pdev; /* Root Port device */
u16 ras_des_offset;
u32 nr_lanes;
struct list_head pmu_node;
struct hlist_node cpuhp_node;
struct perf_event *event[DWC_PCIE_EVENT_TYPE_MAX];
int on_cpu;
};
#define to_dwc_pcie_pmu(p) (container_of(p, struct dwc_pcie_pmu, pmu))
static int dwc_pcie_pmu_hp_state;
static struct list_head dwc_pcie_dev_info_head =
LIST_HEAD_INIT(dwc_pcie_dev_info_head);
static bool notify;
struct dwc_pcie_dev_info {
struct platform_device *plat_dev;
struct pci_dev *pdev;
struct list_head dev_node;
};
struct dwc_pcie_vendor_id {
int vendor_id;
};
static const struct dwc_pcie_vendor_id dwc_pcie_vendor_ids[] = {
{.vendor_id = PCI_VENDOR_ID_ALIBABA },
{.vendor_id = PCI_VENDOR_ID_ROCKCHIP },
{} /* terminator */
};
static ssize_t cpumask_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(dev_get_drvdata(dev));
return cpumap_print_to_pagebuf(true, buf, cpumask_of(pcie_pmu->on_cpu));
}
static DEVICE_ATTR_RO(cpumask);
static struct attribute *dwc_pcie_pmu_cpumask_attrs[] = {
&dev_attr_cpumask.attr,
NULL
};
static struct attribute_group dwc_pcie_cpumask_attr_group = {
.attrs = dwc_pcie_pmu_cpumask_attrs,
};
struct dwc_pcie_format_attr {
struct device_attribute attr;
u64 field;
int config;
};
PMU_FORMAT_ATTR(eventid, "config:0-15");
PMU_FORMAT_ATTR(type, "config:16-19");
PMU_FORMAT_ATTR(lane, "config:20-27");
static struct attribute *dwc_pcie_format_attrs[] = {
&format_attr_type.attr,
&format_attr_eventid.attr,
&format_attr_lane.attr,
NULL,
};
static struct attribute_group dwc_pcie_format_attrs_group = {
.name = "format",
.attrs = dwc_pcie_format_attrs,
};
struct dwc_pcie_event_attr {
struct device_attribute attr;
enum dwc_pcie_event_type type;
u16 eventid;
u8 lane;
};
static ssize_t dwc_pcie_event_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dwc_pcie_event_attr *eattr;
eattr = container_of(attr, typeof(*eattr), attr);
if (eattr->type == DWC_PCIE_LANE_EVENT)
return sysfs_emit(buf, "eventid=0x%x,type=0x%x,lane=?\n",
eattr->eventid, eattr->type);
else if (eattr->type == DWC_PCIE_TIME_BASE_EVENT)
return sysfs_emit(buf, "eventid=0x%x,type=0x%x\n",
eattr->eventid, eattr->type);
return 0;
}
#define DWC_PCIE_EVENT_ATTR(_name, _type, _eventid, _lane) \
(&((struct dwc_pcie_event_attr[]) {{ \
.attr = __ATTR(_name, 0444, dwc_pcie_event_show, NULL), \
.type = _type, \
.eventid = _eventid, \
.lane = _lane, \
}})[0].attr.attr)
#define DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(_name, _eventid) \
DWC_PCIE_EVENT_ATTR(_name, DWC_PCIE_TIME_BASE_EVENT, _eventid, 0)
#define DWC_PCIE_PMU_LANE_EVENT_ATTR(_name, _eventid) \
DWC_PCIE_EVENT_ATTR(_name, DWC_PCIE_LANE_EVENT, _eventid, 0)
static struct attribute *dwc_pcie_pmu_time_event_attrs[] = {
/* Group #0 */
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(one_cycle, 0x00),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(TX_L0S, 0x01),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(RX_L0S, 0x02),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L0, 0x03),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1, 0x04),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_1, 0x05),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_2, 0x06),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(CFG_RCVRY, 0x07),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(TX_RX_L0S, 0x08),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(L1_AUX, 0x09),
/* Group #1 */
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(Tx_PCIe_TLP_Data_Payload, 0x20),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(Rx_PCIe_TLP_Data_Payload, 0x21),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(Tx_CCIX_TLP_Data_Payload, 0x22),
DWC_PCIE_PMU_TIME_BASE_EVENT_ATTR(Rx_CCIX_TLP_Data_Payload, 0x23),
/*
* Leave it to the user to specify the lane ID to avoid generating
* a list of hundreds of events.
*/
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_ack_dllp, 0x600),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_update_fc_dllp, 0x601),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_ack_dllp, 0x602),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_update_fc_dllp, 0x603),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_nulified_tlp, 0x604),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_nulified_tlp, 0x605),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_duplicate_tl, 0x606),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_memory_write, 0x700),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_memory_read, 0x701),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_configuration_write, 0x702),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_configuration_read, 0x703),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_io_write, 0x704),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_io_read, 0x705),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_completion_without_data, 0x706),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_completion_with_data, 0x707),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_message_tlp, 0x708),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_atomic, 0x709),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_tlp_with_prefix, 0x70A),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_memory_write, 0x70B),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_memory_read, 0x70C),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_io_write, 0x70F),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_io_read, 0x710),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_completion_without_data, 0x711),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_completion_with_data, 0x712),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_message_tlp, 0x713),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_atomic, 0x714),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_tlp_with_prefix, 0x715),
DWC_PCIE_PMU_LANE_EVENT_ATTR(tx_ccix_tlp, 0x716),
DWC_PCIE_PMU_LANE_EVENT_ATTR(rx_ccix_tlp, 0x717),
NULL
};
static const struct attribute_group dwc_pcie_event_attrs_group = {
.name = "events",
.attrs = dwc_pcie_pmu_time_event_attrs,
};
static const struct attribute_group *dwc_pcie_attr_groups[] = {
&dwc_pcie_event_attrs_group,
&dwc_pcie_format_attrs_group,
&dwc_pcie_cpumask_attr_group,
NULL
};
static void dwc_pcie_pmu_lane_event_enable(struct dwc_pcie_pmu *pcie_pmu,
bool enable)
{
struct pci_dev *pdev = pcie_pmu->pdev;
u16 ras_des_offset = pcie_pmu->ras_des_offset;
if (enable)
pci_clear_and_set_config_dword(pdev,
ras_des_offset + DWC_PCIE_EVENT_CNT_CTL,
DWC_PCIE_CNT_ENABLE, DWC_PCIE_PER_EVENT_ON);
else
pci_clear_and_set_config_dword(pdev,
ras_des_offset + DWC_PCIE_EVENT_CNT_CTL,
DWC_PCIE_CNT_ENABLE, DWC_PCIE_PER_EVENT_OFF);
}
static void dwc_pcie_pmu_time_based_event_enable(struct dwc_pcie_pmu *pcie_pmu,
bool enable)
{
struct pci_dev *pdev = pcie_pmu->pdev;
u16 ras_des_offset = pcie_pmu->ras_des_offset;
pci_clear_and_set_config_dword(pdev,
ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_CTL,
DWC_PCIE_TIME_BASED_TIMER_START, enable);
}
static u64 dwc_pcie_pmu_read_lane_event_counter(struct perf_event *event)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
struct pci_dev *pdev = pcie_pmu->pdev;
u16 ras_des_offset = pcie_pmu->ras_des_offset;
u32 val;
pci_read_config_dword(pdev, ras_des_offset + DWC_PCIE_EVENT_CNT_DATA, &val);
return val;
}
static u64 dwc_pcie_pmu_read_time_based_counter(struct perf_event *event)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
struct pci_dev *pdev = pcie_pmu->pdev;
int event_id = DWC_PCIE_EVENT_ID(event);
u16 ras_des_offset = pcie_pmu->ras_des_offset;
u32 lo, hi, ss;
u64 val;
/*
* The 64-bit value of the data counter is spread across two
* registers that are not synchronized. In order to read them
* atomically, ensure that the high 32 bits match before and after
* reading the low 32 bits.
*/
pci_read_config_dword(pdev,
ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_DATA_REG_HIGH, &hi);
do {
/* snapshot the high 32 bits */
ss = hi;
pci_read_config_dword(
pdev, ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_DATA_REG_LOW,
&lo);
pci_read_config_dword(
pdev, ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_DATA_REG_HIGH,
&hi);
} while (hi != ss);
val = ((u64)hi << 32) | lo;
/*
* The Group#1 event measures the amount of data processed in 16-byte
* units. Simplify the end-user interface by multiplying the counter
* at the point of read.
*/
if (event_id >= 0x20 && event_id <= 0x23)
val *= 16;
return val;
}
static void dwc_pcie_pmu_event_update(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event);
u64 delta, prev, now = 0;
do {
prev = local64_read(&hwc->prev_count);
if (type == DWC_PCIE_LANE_EVENT)
now = dwc_pcie_pmu_read_lane_event_counter(event);
else if (type == DWC_PCIE_TIME_BASE_EVENT)
now = dwc_pcie_pmu_read_time_based_counter(event);
} while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
delta = (now - prev) & DWC_PCIE_MAX_PERIOD;
/* 32-bit counter for Lane Event Counting */
if (type == DWC_PCIE_LANE_EVENT)
delta &= DWC_PCIE_LANE_EVENT_MAX_PERIOD;
local64_add(delta, &event->count);
}
static int dwc_pcie_pmu_event_init(struct perf_event *event)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event);
struct perf_event *sibling;
u32 lane;
if (event->attr.type != event->pmu->type)
return -ENOENT;
/* We don't support sampling */
if (is_sampling_event(event))
return -EINVAL;
/* We cannot support task bound events */
if (event->cpu < 0 || event->attach_state & PERF_ATTACH_TASK)
return -EINVAL;
if (event->group_leader != event &&
!is_software_event(event->group_leader))
return -EINVAL;
for_each_sibling_event(sibling, event->group_leader) {
if (sibling->pmu != event->pmu && !is_software_event(sibling))
return -EINVAL;
}
if (type < 0 || type >= DWC_PCIE_EVENT_TYPE_MAX)
return -EINVAL;
if (type == DWC_PCIE_LANE_EVENT) {
lane = DWC_PCIE_EVENT_LANE(event);
if (lane < 0 || lane >= pcie_pmu->nr_lanes)
return -EINVAL;
}
event->cpu = pcie_pmu->on_cpu;
return 0;
}
static void dwc_pcie_pmu_event_start(struct perf_event *event, int flags)
{
struct hw_perf_event *hwc = &event->hw;
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event);
hwc->state = 0;
local64_set(&hwc->prev_count, 0);
if (type == DWC_PCIE_LANE_EVENT)
dwc_pcie_pmu_lane_event_enable(pcie_pmu, true);
else if (type == DWC_PCIE_TIME_BASE_EVENT)
dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true);
}
static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event);
struct hw_perf_event *hwc = &event->hw;
if (event->hw.state & PERF_HES_STOPPED)
return;
if (type == DWC_PCIE_LANE_EVENT)
dwc_pcie_pmu_lane_event_enable(pcie_pmu, false);
else if (type == DWC_PCIE_TIME_BASE_EVENT)
dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false);
dwc_pcie_pmu_event_update(event);
hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
}
static int dwc_pcie_pmu_event_add(struct perf_event *event, int flags)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
struct pci_dev *pdev = pcie_pmu->pdev;
struct hw_perf_event *hwc = &event->hw;
enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event);
int event_id = DWC_PCIE_EVENT_ID(event);
int lane = DWC_PCIE_EVENT_LANE(event);
u16 ras_des_offset = pcie_pmu->ras_des_offset;
u32 ctrl;
/* one counter for each type and it is in use */
if (pcie_pmu->event[type])
return -ENOSPC;
pcie_pmu->event[type] = event;
hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
if (type == DWC_PCIE_LANE_EVENT) {
/* EVENT_COUNTER_DATA_REG needs clear manually */
ctrl = FIELD_PREP(DWC_PCIE_CNT_EVENT_SEL, event_id) |
FIELD_PREP(DWC_PCIE_CNT_LANE_SEL, lane) |
FIELD_PREP(DWC_PCIE_CNT_ENABLE, DWC_PCIE_PER_EVENT_OFF) |
FIELD_PREP(DWC_PCIE_EVENT_CLEAR, DWC_PCIE_EVENT_PER_CLEAR);
pci_write_config_dword(pdev, ras_des_offset + DWC_PCIE_EVENT_CNT_CTL,
ctrl);
} else if (type == DWC_PCIE_TIME_BASE_EVENT) {
/*
* TIME_BASED_ANAL_DATA_REG is a 64 bit register, we can safely
* use it with any manually controlled duration. And it is
* cleared when next measurement starts.
*/
ctrl = FIELD_PREP(DWC_PCIE_TIME_BASED_REPORT_SEL, event_id) |
FIELD_PREP(DWC_PCIE_TIME_BASED_DURATION_SEL,
DWC_PCIE_DURATION_MANUAL_CTL) |
DWC_PCIE_TIME_BASED_CNT_ENABLE;
pci_write_config_dword(
pdev, ras_des_offset + DWC_PCIE_TIME_BASED_ANAL_CTL, ctrl);
}
if (flags & PERF_EF_START)
dwc_pcie_pmu_event_start(event, PERF_EF_RELOAD);
perf_event_update_userpage(event);
return 0;
}
static void dwc_pcie_pmu_event_del(struct perf_event *event, int flags)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
enum dwc_pcie_event_type type = DWC_PCIE_EVENT_TYPE(event);
dwc_pcie_pmu_event_stop(event, flags | PERF_EF_UPDATE);
perf_event_update_userpage(event);
pcie_pmu->event[type] = NULL;
}
static void dwc_pcie_pmu_remove_cpuhp_instance(void *hotplug_node)
{
cpuhp_state_remove_instance_nocalls(dwc_pcie_pmu_hp_state, hotplug_node);
}
/*
* Find the binded DES capability device info of a PCI device.
* @pdev: The PCI device.
*/
static struct dwc_pcie_dev_info *dwc_pcie_find_dev_info(struct pci_dev *pdev)
{
struct dwc_pcie_dev_info *dev_info;
list_for_each_entry(dev_info, &dwc_pcie_dev_info_head, dev_node)
if (dev_info->pdev == pdev)
return dev_info;
return NULL;
}
static void dwc_pcie_unregister_pmu(void *data)
{
struct dwc_pcie_pmu *pcie_pmu = data;
perf_pmu_unregister(&pcie_pmu->pmu);
}
static bool dwc_pcie_match_des_cap(struct pci_dev *pdev)
{
const struct dwc_pcie_vendor_id *vid;
u16 vsec = 0;
u32 val;
if (!pci_is_pcie(pdev) || !(pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT))
return false;
for (vid = dwc_pcie_vendor_ids; vid->vendor_id; vid++) {
vsec = pci_find_vsec_capability(pdev, vid->vendor_id,
DWC_PCIE_VSEC_RAS_DES_ID);
if (vsec)
break;
}
if (!vsec)
return false;
pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
if (PCI_VNDR_HEADER_REV(val) != 0x04)
return false;
pci_dbg(pdev,
"Detected PCIe Vendor-Specific Extended Capability RAS DES\n");
return true;
}
static void dwc_pcie_unregister_dev(struct dwc_pcie_dev_info *dev_info)
{
platform_device_unregister(dev_info->plat_dev);
list_del(&dev_info->dev_node);
kfree(dev_info);
}
static int dwc_pcie_register_dev(struct pci_dev *pdev)
{
struct platform_device *plat_dev;
struct dwc_pcie_dev_info *dev_info;
u32 bdf;
bdf = PCI_DEVID(pdev->bus->number, pdev->devfn);
plat_dev = platform_device_register_data(NULL, "dwc_pcie_pmu", bdf,
pdev, sizeof(*pdev));
if (IS_ERR(plat_dev))
return PTR_ERR(plat_dev);
dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
if (!dev_info)
return -ENOMEM;
/* Cache platform device to handle pci device hotplug */
dev_info->plat_dev = plat_dev;
dev_info->pdev = pdev;
list_add(&dev_info->dev_node, &dwc_pcie_dev_info_head);
return 0;
}
static int dwc_pcie_pmu_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct device *dev = data;
struct pci_dev *pdev = to_pci_dev(dev);
struct dwc_pcie_dev_info *dev_info;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
if (!dwc_pcie_match_des_cap(pdev))
return NOTIFY_DONE;
if (dwc_pcie_register_dev(pdev))
return NOTIFY_BAD;
break;
case BUS_NOTIFY_DEL_DEVICE:
dev_info = dwc_pcie_find_dev_info(pdev);
if (!dev_info)
return NOTIFY_DONE;
dwc_pcie_unregister_dev(dev_info);
break;
}
return NOTIFY_OK;
}
static struct notifier_block dwc_pcie_pmu_nb = {
.notifier_call = dwc_pcie_pmu_notifier,
};
static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
{
struct pci_dev *pdev = plat_dev->dev.platform_data;
struct dwc_pcie_pmu *pcie_pmu;
char *name;
u32 bdf, val;
u16 vsec;
int ret;
vsec = pci_find_vsec_capability(pdev, pdev->vendor,
DWC_PCIE_VSEC_RAS_DES_ID);
pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
bdf = PCI_DEVID(pdev->bus->number, pdev->devfn);
name = devm_kasprintf(&plat_dev->dev, GFP_KERNEL, "dwc_rootport_%x", bdf);
if (!name)
return -ENOMEM;
pcie_pmu = devm_kzalloc(&plat_dev->dev, sizeof(*pcie_pmu), GFP_KERNEL);
if (!pcie_pmu)
return -ENOMEM;
pcie_pmu->pdev = pdev;
pcie_pmu->ras_des_offset = vsec;
pcie_pmu->nr_lanes = pcie_get_width_cap(pdev);
pcie_pmu->on_cpu = -1;
pcie_pmu->pmu = (struct pmu){
.name = name,
.module = THIS_MODULE,
.attr_groups = dwc_pcie_attr_groups,
.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
.task_ctx_nr = perf_invalid_context,
.event_init = dwc_pcie_pmu_event_init,
.add = dwc_pcie_pmu_event_add,
.del = dwc_pcie_pmu_event_del,
.start = dwc_pcie_pmu_event_start,
.stop = dwc_pcie_pmu_event_stop,
.read = dwc_pcie_pmu_event_update,
};
/* Add this instance to the list used by the offline callback */
ret = cpuhp_state_add_instance(dwc_pcie_pmu_hp_state,
&pcie_pmu->cpuhp_node);
if (ret) {
pci_err(pdev, "Error %d registering hotplug @%x\n", ret, bdf);
return ret;
}
/* Unwind when platform driver removes */
ret = devm_add_action_or_reset(&plat_dev->dev,
dwc_pcie_pmu_remove_cpuhp_instance,
&pcie_pmu->cpuhp_node);
if (ret)
return ret;
ret = perf_pmu_register(&pcie_pmu->pmu, name, -1);
if (ret) {
pci_err(pdev, "Error %d registering PMU @%x\n", ret, bdf);
return ret;
}
ret = devm_add_action_or_reset(&plat_dev->dev, dwc_pcie_unregister_pmu,
pcie_pmu);
if (ret)
return ret;
return 0;
}
static int dwc_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *cpuhp_node)
{
struct dwc_pcie_pmu *pcie_pmu;
pcie_pmu = hlist_entry_safe(cpuhp_node, struct dwc_pcie_pmu, cpuhp_node);
if (pcie_pmu->on_cpu == -1)
pcie_pmu->on_cpu = cpumask_local_spread(
0, dev_to_node(&pcie_pmu->pdev->dev));
return 0;
}
static int dwc_pcie_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_node)
{
struct dwc_pcie_pmu *pcie_pmu;
struct pci_dev *pdev;
int node;
cpumask_t mask;
unsigned int target;
pcie_pmu = hlist_entry_safe(cpuhp_node, struct dwc_pcie_pmu, cpuhp_node);
/* Nothing to do if this CPU doesn't own the PMU */
if (cpu != pcie_pmu->on_cpu)
return 0;
pcie_pmu->on_cpu = -1;
pdev = pcie_pmu->pdev;
node = dev_to_node(&pdev->dev);
if (cpumask_and(&mask, cpumask_of_node(node), cpu_online_mask) &&
cpumask_andnot(&mask, &mask, cpumask_of(cpu)))
target = cpumask_any(&mask);
else
target = cpumask_any_but(cpu_online_mask, cpu);
if (target >= nr_cpu_ids) {
pci_err(pdev, "There is no CPU to set\n");
return 0;
}
/* This PMU does NOT support interrupt, just migrate context. */
perf_pmu_migrate_context(&pcie_pmu->pmu, cpu, target);
pcie_pmu->on_cpu = target;
return 0;
}
static struct platform_driver dwc_pcie_pmu_driver = {
.probe = dwc_pcie_pmu_probe,
.driver = {.name = "dwc_pcie_pmu",},
};
static int __init dwc_pcie_pmu_init(void)
{
struct pci_dev *pdev = NULL;
bool found = false;
int ret;
for_each_pci_dev(pdev) {
if (!dwc_pcie_match_des_cap(pdev))
continue;
ret = dwc_pcie_register_dev(pdev);
if (ret) {
pci_dev_put(pdev);
return ret;
}
found = true;
}
if (!found)
return -ENODEV;
ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
"perf/dwc_pcie_pmu:online",
dwc_pcie_pmu_online_cpu,
dwc_pcie_pmu_offline_cpu);
if (ret < 0)
return ret;
dwc_pcie_pmu_hp_state = ret;
ret = platform_driver_register(&dwc_pcie_pmu_driver);
if (ret)
goto platform_driver_register_err;
ret = bus_register_notifier(&pci_bus_type, &dwc_pcie_pmu_nb);
if (ret)
goto platform_driver_register_err;
notify = true;
return 0;
platform_driver_register_err:
cpuhp_remove_multi_state(dwc_pcie_pmu_hp_state);
return ret;
}
static void __exit dwc_pcie_pmu_exit(void)
{
struct dwc_pcie_dev_info *dev_info, *tmp;
if (notify)
bus_unregister_notifier(&pci_bus_type, &dwc_pcie_pmu_nb);
list_for_each_entry_safe(dev_info, tmp, &dwc_pcie_dev_info_head, dev_node)
dwc_pcie_unregister_dev(dev_info);
platform_driver_unregister(&dwc_pcie_pmu_driver);
cpuhp_remove_multi_state(dwc_pcie_pmu_hp_state);
}
module_init(dwc_pcie_pmu_init);
module_exit(dwc_pcie_pmu_exit);
MODULE_DESCRIPTION("PMU driver for DesignWare Cores PCI Express Controller");
MODULE_AUTHOR("Shuai Xue <xueshuai@linux.alibaba.com>");
MODULE_LICENSE("GPL v2");

View File

@@ -32,7 +32,8 @@
#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0
#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904
#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04
#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0))
#define RK3588_SRAM_INIT_DONE(reg) ((reg & 0xff) == 0x49)
#define RK3588_SRAM_INIT_TIMEOUT 20000
struct rockchip_p3phy_ops;
@@ -148,11 +149,14 @@ static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv)
ret = regmap_read_poll_timeout(priv->phy_grf,
RK3588_PCIE3PHY_GRF_PHY0_STATUS1,
reg, RK3588_SRAM_INIT_DONE(reg),
0, 500);
ret |= regmap_read_poll_timeout(priv->phy_grf,
RK3588_PCIE3PHY_GRF_PHY1_STATUS1,
reg, RK3588_SRAM_INIT_DONE(reg),
0, 500);
100, RK3588_SRAM_INIT_TIMEOUT);
if (priv->pcie30_phymode == PHY_MODE_PCIE_AGGREGATION) {
ret |= regmap_read_poll_timeout(priv->phy_grf,
RK3588_PCIE3PHY_GRF_PHY1_STATUS1,
reg, RK3588_SRAM_INIT_DONE(reg),
100, RK3588_SRAM_INIT_TIMEOUT);
}
if (ret)
dev_err(&priv->phy->dev, "%s: lock failed 0x%x, check input refclk and power supply\n",
__func__, reg);

View File

@@ -295,12 +295,12 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
/* Keep things powered as long as CS is asserted */
pm_runtime_get_sync(rs->dev);
if (spi->cs_gpiod)
if (spi_get_csgpiod(spi, 0))
ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
else
ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select));
} else {
if (spi->cs_gpiod)
if (spi_get_csgpiod(spi, 0))
ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
else
ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select));
@@ -642,7 +642,7 @@ static int rockchip_spi_config(struct rockchip_spi *rs,
cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET;
if (spi->mode & SPI_LSB_FIRST)
cr0 |= CR0_FBM_LSB << CR0_FBM_OFFSET;
if (spi->mode & SPI_CS_HIGH)
if (spi->mode & SPI_CS_HIGH && !spi_get_csgpiod(spi, 0))
cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET;
if (xfer->rx_buf && xfer->tx_buf) {
@@ -903,20 +903,15 @@ static int rockchip_spi_setup(struct spi_device *spi)
struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller);
u32 cr0;
if (!spi->cs_gpiod && (spi->mode & SPI_CS_HIGH) && !rs->cs_high_supported) {
dev_warn(&spi->dev, "setup: non GPIO CS can't be active-high\n");
return -EINVAL;
}
pm_runtime_get_sync(rs->dev);
cr0 = readl_relaxed(rs->regs + ROCKCHIP_SPI_CTRLR0);
cr0 &= ~(0x3 << CR0_SCPH_OFFSET);
cr0 |= ((spi->mode & 0x3) << CR0_SCPH_OFFSET);
if (spi->mode & SPI_CS_HIGH && spi->chip_select <= 1)
if (spi->mode & SPI_CS_HIGH && spi->chip_select <= 1 && !spi_get_csgpiod(spi, 0))
cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET;
else if (spi->chip_select <= 1)
else if (spi->chip_select <= 1 && !spi_get_csgpiod(spi, 0))
cr0 &= ~(BIT(spi->chip_select) << CR0_SOI_OFFSET);
if (spi_controller_is_slave(spi->controller))
cr0 |= CR0_OPM_SLAVE << CR0_OPM_OFFSET;

View File

@@ -1911,6 +1911,8 @@ static const struct rockchip_tsadc_chip rk3506_tsadc_data = {
.set_alarm_temp = rk_tsadcv3_alarm_temp,
.set_tshut_temp = rk_tsadcv3_tshut_temp,
.set_tshut_mode = rk_tsadcv4_tshut_mode,
.get_trim_code = rk_tsadcv2_get_trim_code,
.trim_slope = 594,
.table = {
.id = rk3506_code_table,
.length = ARRAY_SIZE(rk3506_code_table),

View File

@@ -17,6 +17,7 @@ static inline bool pcie_aspm_ext_is_in_l1sub_state(struct pci_dev *pdev) { retur
enum rockchip_pcie_pm_ctrl_flag {
ROCKCHIP_PCIE_PM_CTRL_RESET = 1,
ROCKCHIP_PCIE_PM_RETRAIN_LINK = 2,
};
int rockchip_dw_pcie_pm_ctrl_for_user(struct pci_dev *dev, enum rockchip_pcie_pm_ctrl_flag flag);

View File

@@ -1230,6 +1230,8 @@ int pci_read_config_dword(const struct pci_dev *dev, int where, u32 *val);
int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val);
int pci_write_config_word(const struct pci_dev *dev, int where, u16 val);
int pci_write_config_dword(const struct pci_dev *dev, int where, u32 val);
void pci_clear_and_set_config_dword(const struct pci_dev *dev, int pos,
u32 clear, u32 set);
int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val);
int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val);

View File

@@ -2592,6 +2592,8 @@
#define PCI_VENDOR_ID_TEKRAM 0x1de1
#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29
#define PCI_VENDOR_ID_ALIBABA 0x1ded
#define PCI_VENDOR_ID_TEHUTI 0x1fc9
#define PCI_DEVICE_ID_TEHUTI_3009 0x3009
#define PCI_DEVICE_ID_TEHUTI_3010 0x3010