diff options
| author | Mantravadi Karthik <mkarthik@nvidia.com> | 2021-05-20 13:19:26 -0400 |
|---|---|---|
| committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2021-06-02 16:39:07 -0400 |
| commit | d9ca6d86e9a96e966a111ff1edd421c095b14ffa (patch) | |
| tree | ce033b0f26695edec95520be0c65589f9ee32052 /drivers/platform/tegra | |
| parent | 28114d7d9fc9192a827c0daed4199f6f0a724b3c (diff) | |
tegra: safety-ivc: Add support for HB CMD
Why?
L2SS expects a heartbeat ping every 40ms. CCPLEX is
expected to write the Boot status in the IVC channel
for the first time and the consective pings will be
just to send Alive check.
How?
Create a new callback API for the HB command which
fills the bit fields for the HB data and sends it over
the cmd-resp IVC channel.
* Added mutex lock support for l1ss_cmd_resp_send_frame
for syncronizing the ivc writes.
* Waiting for empty interrupts hogs the system. Time
critical features such as HB fail while waiting for
empty interrupts. Hence, removing the HSP SM empty isr
support.
Bug 200700400
Change-Id: I7f124c9f7336df9d387536aa3f2dda80d9234db8
Signed-off-by: Mantravadi Karthik <mkarthik@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2519655
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/platform/tegra')
| -rw-r--r-- | drivers/platform/tegra/tegra-safety-ivc.c | 81 | ||||
| -rw-r--r-- | drivers/platform/tegra/tegra_l1ss.c | 64 | ||||
| -rw-r--r-- | drivers/platform/tegra/tegra_l1ss.h | 24 | ||||
| -rw-r--r-- | drivers/platform/tegra/tegra_l1ss_cmd_resp_exec_config.h | 2 | ||||
| -rw-r--r-- | drivers/platform/tegra/tegra_l1ss_cmd_resp_l2_interface.c | 102 | ||||
| -rw-r--r-- | drivers/platform/tegra/tegra_l1ss_cmd_resp_l2_interface.h | 4 | ||||
| -rw-r--r-- | drivers/platform/tegra/tegra_l1ss_heartbeat.h | 217 |
7 files changed, 410 insertions, 84 deletions
diff --git a/drivers/platform/tegra/tegra-safety-ivc.c b/drivers/platform/tegra/tegra-safety-ivc.c index 85bbd8bfd..d468639d9 100644 --- a/drivers/platform/tegra/tegra-safety-ivc.c +++ b/drivers/platform/tegra/tegra-safety-ivc.c | |||
| @@ -29,7 +29,6 @@ | |||
| 29 | #include "tegra_l1ss.h" | 29 | #include "tegra_l1ss.h" |
| 30 | 30 | ||
| 31 | #define NV(p) "nvidia," #p | 31 | #define NV(p) "nvidia," #p |
| 32 | #define TEGRA_SAFETY_IVC_READ_TIMEOUT 2 * HZ | ||
| 33 | 32 | ||
| 34 | int ivc_chan_count; | 33 | int ivc_chan_count; |
| 35 | struct tegra_safety_ivc_chan *tegra_safety_get_ivc_chan_from_str( | 34 | struct tegra_safety_ivc_chan *tegra_safety_get_ivc_chan_from_str( |
| @@ -60,18 +59,29 @@ static void tegra_safety_decode_cmd_resp(struct tegra_safety_ivc *safety_ivc) | |||
| 60 | } | 59 | } |
| 61 | 60 | ||
| 62 | ivc = &ivc_ch->ivc; | 61 | ivc = &ivc_ch->ivc; |
| 63 | if (tegra_ivc_can_read(ivc)) { | 62 | mutex_lock(&safety_ivc->rlock); |
| 64 | pr_err("%s: Reading IVC channel\n", __func__); | 63 | if (tegra_ivc_can_read(ivc)) |
| 65 | ivc_frame = tegra_ivc_read_get_next_frame(ivc); | 64 | ivc_frame = tegra_ivc_read_get_next_frame(ivc); |
| 66 | if (ivc_frame == NULL) | 65 | mutex_unlock(&safety_ivc->rlock); |
| 67 | pr_err("%s: No IVC frame to read\n", __func__); | ||
| 68 | 66 | ||
| 69 | memcpy(cmd_resp, ivc_frame, sizeof(cmdresp_frame_ex_t)); | 67 | if (ivc_frame == NULL) { |
| 70 | 68 | pr_err("%s: No IVC frame to read\n", __func__); | |
| 71 | tegra_ivc_read_advance(ivc); | 69 | return; |
| 72 | tegra_safety_handle_cmd((cmdresp_frame_ex_t *)&cmd_resp, | ||
| 73 | safety_ivc->ldata); | ||
| 74 | } | 70 | } |
| 71 | |||
| 72 | memcpy(cmd_resp, ivc_frame, sizeof(cmdresp_frame_ex_t)); | ||
| 73 | |||
| 74 | tegra_ivc_read_advance(ivc); | ||
| 75 | tegra_safety_handle_cmd((cmdresp_frame_ex_t *)&cmd_resp, | ||
| 76 | safety_ivc->ldata); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void tegra_safety_cmdresp_work_func(struct work_struct *work) | ||
| 80 | { | ||
| 81 | struct tegra_safety_ivc *safety_ivc = container_of(work, | ||
| 82 | struct tegra_safety_ivc, work); | ||
| 83 | |||
| 84 | tegra_safety_decode_cmd_resp(safety_ivc); | ||
| 75 | } | 85 | } |
| 76 | 86 | ||
| 77 | /* wake up cmd-resp threads */ | 87 | /* wake up cmd-resp threads */ |
| @@ -86,34 +96,10 @@ static u32 tegra_safety_ivc_full_notify(void *data, u32 response) | |||
| 86 | l1ss_set_ivc_ready(); | 96 | l1ss_set_ivc_ready(); |
| 87 | wake_up(&safety_ivc->cmd.response_waitq); | 97 | wake_up(&safety_ivc->cmd.response_waitq); |
| 88 | l1ss_notify_client(L1SS_READY); | 98 | l1ss_notify_client(L1SS_READY); |
| 89 | } else if (response == 0) { | 99 | } else if (response == TEGRA_SAFETY_SM_CMDRESP_CH) { |
| 90 | tegra_safety_decode_cmd_resp(safety_ivc); | 100 | queue_work(safety_ivc->wq, &safety_ivc->work); |
| 91 | } | 101 | } else |
| 92 | 102 | pr_err("%s: Invalid response %d received", __func__, response); | |
| 93 | return 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | static void tegra_safety_ivc_empty_notify(void *data, u32 response) | ||
| 97 | { | ||
| 98 | struct tegra_safety_ivc *safety_ivc = (struct tegra_safety_ivc *)data; | ||
| 99 | wake_up(&safety_ivc->cmd.empty_waitq); | ||
| 100 | } | ||
| 101 | |||
| 102 | static int tegra_safety_ivc_command(struct device *dev, u32 command, | ||
| 103 | long timeout) | ||
| 104 | { | ||
| 105 | struct tegra_safety_ivc *safety_ivc = dev_get_drvdata(dev); | ||
| 106 | |||
| 107 | tegra_hsp_sm_pair_write(safety_ivc->ivc_pair, command); | ||
| 108 | |||
| 109 | timeout = wait_event_interruptible_timeout( | ||
| 110 | safety_ivc->cmd.empty_waitq, | ||
| 111 | tegra_hsp_sm_pair_is_empty(safety_ivc->ivc_pair), | ||
| 112 | timeout); | ||
| 113 | if (timeout <= 0) { | ||
| 114 | dev_err(dev, "Timed out waiting for empty SM"); | ||
| 115 | return -ETIMEDOUT; | ||
| 116 | } | ||
| 117 | 103 | ||
| 118 | return 0; | 104 | return 0; |
| 119 | } | 105 | } |
| @@ -128,10 +114,7 @@ static int tegra_safety_ivc_setup_ready(struct device *dev) | |||
| 128 | 114 | ||
| 129 | command = SAFETY_CONF(IVC_READY, (region->dma >> 8)); | 115 | command = SAFETY_CONF(IVC_READY, (region->dma >> 8)); |
| 130 | 116 | ||
| 131 | ret = tegra_safety_ivc_command(dev, command, | 117 | tegra_hsp_sm_pair_write(safety_ivc->ivc_pair, command); |
| 132 | TEGRA_SAFETY_IVC_READ_TIMEOUT); | ||
| 133 | if (ret < 0) | ||
| 134 | return ret; | ||
| 135 | 118 | ||
| 136 | timeout = wait_event_interruptible_timeout( | 119 | timeout = wait_event_interruptible_timeout( |
| 137 | safety_ivc->cmd.response_waitq, | 120 | safety_ivc->cmd.response_waitq, |
| @@ -151,7 +134,8 @@ static void tegra_ivc_channel_ring(struct ivc *ivc) | |||
| 151 | container_of(ivc, struct tegra_safety_ivc_chan, ivc); | 134 | container_of(ivc, struct tegra_safety_ivc_chan, ivc); |
| 152 | struct tegra_safety_ivc *safety_ivc = ivc_chan->safety_ivc; | 135 | struct tegra_safety_ivc *safety_ivc = ivc_chan->safety_ivc; |
| 153 | 136 | ||
| 154 | tegra_hsp_sm_pair_write(safety_ivc->ivc_pair, 1); | 137 | tegra_hsp_sm_pair_write(safety_ivc->ivc_pair, |
| 138 | TEGRA_SAFETY_SM_CMDRESP_CH); | ||
| 155 | } | 139 | } |
| 156 | 140 | ||
| 157 | static int tegra_ivc_channel_create( | 141 | static int tegra_ivc_channel_create( |
| @@ -325,7 +309,7 @@ static int tegra_safety_ivc_parse_hsp(struct device *dev) | |||
| 325 | hsp_node = of_get_child_by_name(dev->of_node, "hsp"); | 309 | hsp_node = of_get_child_by_name(dev->of_node, "hsp"); |
| 326 | safety_ivc->ivc_pair = of_tegra_hsp_sm_pair_by_name(hsp_node, | 310 | safety_ivc->ivc_pair = of_tegra_hsp_sm_pair_by_name(hsp_node, |
| 327 | "ivc-pair", tegra_safety_ivc_full_notify, | 311 | "ivc-pair", tegra_safety_ivc_full_notify, |
| 328 | tegra_safety_ivc_empty_notify, safety_ivc); | 312 | NULL, safety_ivc); |
| 329 | of_node_put(hsp_node); | 313 | of_node_put(hsp_node); |
| 330 | if (IS_ERR(safety_ivc->ivc_pair)) { | 314 | if (IS_ERR(safety_ivc->ivc_pair)) { |
| 331 | ret = PTR_ERR(safety_ivc->ivc_pair); | 315 | ret = PTR_ERR(safety_ivc->ivc_pair); |
| @@ -359,6 +343,7 @@ static int tegra_safety_ivc_remove(struct platform_device *pdev) | |||
| 359 | 343 | ||
| 360 | tegra_safety_ast_region_free(dev); | 344 | tegra_safety_ast_region_free(dev); |
| 361 | tegra_hsp_sm_pair_free(safety_ivc->ivc_pair); | 345 | tegra_hsp_sm_pair_free(safety_ivc->ivc_pair); |
| 346 | destroy_workqueue(safety_ivc->wq); | ||
| 362 | ivc_chan_count = 0; | 347 | ivc_chan_count = 0; |
| 363 | 348 | ||
| 364 | return 0; | 349 | return 0; |
| @@ -370,7 +355,7 @@ static int tegra_safety_ivc_probe(struct platform_device *pdev) | |||
| 370 | struct device *dev = &pdev->dev; | 355 | struct device *dev = &pdev->dev; |
| 371 | int ret, i; | 356 | int ret, i; |
| 372 | 357 | ||
| 373 | dev_info(dev, "probing sce safety driver\n"); | 358 | dev_info(dev, "Probing sce safety driver\n"); |
| 374 | 359 | ||
| 375 | safety_ivc = devm_kzalloc(dev, sizeof(*safety_ivc), GFP_KERNEL); | 360 | safety_ivc = devm_kzalloc(dev, sizeof(*safety_ivc), GFP_KERNEL); |
| 376 | if (!safety_ivc) | 361 | if (!safety_ivc) |
| @@ -380,6 +365,10 @@ static int tegra_safety_ivc_probe(struct platform_device *pdev) | |||
| 380 | 365 | ||
| 381 | init_waitqueue_head(&safety_ivc->cmd.response_waitq); | 366 | init_waitqueue_head(&safety_ivc->cmd.response_waitq); |
| 382 | init_waitqueue_head(&safety_ivc->cmd.empty_waitq); | 367 | init_waitqueue_head(&safety_ivc->cmd.empty_waitq); |
| 368 | safety_ivc->wq = alloc_workqueue("safety_cmdresp", WQ_HIGHPRI, 0); | ||
| 369 | INIT_WORK(&safety_ivc->work, tegra_safety_cmdresp_work_func); | ||
| 370 | mutex_init(&safety_ivc->rlock); | ||
| 371 | mutex_init(&safety_ivc->wlock); | ||
| 383 | 372 | ||
| 384 | ret = tegra_safety_ivc_parse_hsp(dev); | 373 | ret = tegra_safety_ivc_parse_hsp(dev); |
| 385 | if (ret) { | 374 | if (ret) { |
| @@ -420,7 +409,7 @@ static int tegra_safety_ivc_probe(struct platform_device *pdev) | |||
| 420 | } | 409 | } |
| 421 | } | 410 | } |
| 422 | 411 | ||
| 423 | dev_info(dev, "successfully probed safety ivc driver\n"); | 412 | dev_info(dev, "Successfully probed safety ivc driver\n"); |
| 424 | |||
