-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsbl_pml_serdes_op.c
138 lines (107 loc) · 3.44 KB
/
sbl_pml_serdes_op.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019-2023 Hewlett Packard Enterprise Development LP */
//#define DEBUG 1
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <sbl/sbl_pml.h>
#include "sbl_kconfig.h"
#include "sbl.h"
#include "sbl_serdes.h"
#include "sbl_internal.h"
static inline bool sbl_pml_serdes_op_busy(struct sbl_inst *sbl, int port_num);
/*
* Perform a serdes "operation"
*
* Essentially write the op code and its data to a register
* then poll for completion bit
*
* We use a mutex as we don't know how long the operation might take so we
* might need to sleep. Timing is very approximate as its not very critical.
*
* Currently we lock this to enforce a single operation - we can probably relax this
* now its in the sbl
*/
int sbl_pml_serdes_op(struct sbl_inst *sbl, int port_num, u64 serdes_sel,
u64 op, u64 data, u16 *result, int timeout, unsigned int flags)
{
struct sbl_link *link;
u32 base = SBL_PML_BASE(port_num);
u64 val64;
int delay;
int poll_interval;
unsigned long last_jiffy;
int err;
err = sbl_validate_instance(sbl);
if (err)
return err;
err = sbl_validate_port_num(sbl, port_num);
if (err)
return err;
sbl_dev_dbg(sbl->dev, "serdes op, p%ds%lld, %lld, %lld, %d 0x%x\n",
port_num, serdes_sel, op, data, timeout, flags);
if (!result)
return -EINVAL;
/* cannot wait infinitely */
if (!timeout)
return -EINVAL;
/* a polling interval is mandatory */
poll_interval = sbl_flags_get_poll_interval_from_flags(flags);
if (!poll_interval)
return -EINVAL;
delay = sbl_flags_get_delay_from_flags(flags);
link = sbl->link + port_num;
err = mutex_lock_interruptible(&link->serdes_mtx);
if (err)
return -ERESTARTSYS;
if (sbl_pml_serdes_op_busy(sbl, port_num)) {
mutex_unlock(&link->serdes_mtx);
return -EBUSY;
}
/* start the operation */
val64 = SBL_PML_SERDES_CORE_INTERRUPT_SET(serdes_sel, 1ULL, op, data);
sbl_write64(sbl, base|SBL_PML_SERDES_CORE_INTERRUPT_OFFSET, val64);
sbl_read64(sbl, base|SBL_PML_SERDES_CORE_INTERRUPT_OFFSET); /* flush */
if (delay)
udelay(delay);
/* poll for completion or timeout */
last_jiffy = jiffies + msecs_to_jiffies(timeout) + 1;
while (sbl_pml_serdes_op_busy(sbl, port_num)) {
if (time_is_before_jiffies(last_jiffy)) {
mutex_unlock(&link->serdes_mtx);
return -ETIMEDOUT;
}
msleep(poll_interval);
}
/* get the result */
val64 = sbl_read64(sbl, base|SBL_PML_SERDES_CORE_INTERRUPT_OFFSET);
*result = SBL_PML_SERDES_CORE_INTERRUPT_CORE_INTERRUPT_DATA_GET(val64);
mutex_unlock(&link->serdes_mtx);
return 0;
}
EXPORT_SYMBOL(sbl_pml_serdes_op);
static inline bool sbl_pml_serdes_op_busy(struct sbl_inst *sbl, int port_num)
{
u64 val64;
val64 = sbl_read64(sbl, SBL_PML_BASE(port_num)|
SBL_PML_SERDES_CORE_INTERRUPT_OFFSET);
return SBL_PML_SERDES_CORE_INTERRUPT_DO_CORE_INTERRUPT_GET(val64);
}
/*
* serdes core interrupt access timings
*/
int sbl_pml_serdes_op_timing(struct sbl_inst *sbl, int port_num, u64 capture,
u64 clear, u64 set)
{
u32 base = SBL_PML_BASE(port_num);
u64 val64;
val64 = SBL_PML_CFG_SERDES_CORE_INTERRUPT_CAPTURE_INTERRUPT_DATA_DELAY_SET(capture) |
SBL_PML_CFG_SERDES_CORE_INTERRUPT_CLEAR_INTERRUPT_DELAY_SET(clear) |
SBL_PML_CFG_SERDES_CORE_INTERRUPT_SET_INTERRUPT_DELAY_SET(set);
sbl_write64(sbl, base|SBL_PML_CFG_SERDES_CORE_INTERRUPT_OFFSET,
val64);
return 0;
}
EXPORT_SYMBOL(sbl_pml_serdes_op_timing);