mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
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:
@@ -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";
|
||||
};
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -203,6 +203,7 @@
|
||||
};
|
||||
|
||||
&u2phy_otg0 {
|
||||
rockchip,vbus-always-on;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
792
drivers/perf/dwc_pcie_pmu.c
Normal 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");
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user