1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ddbridge-mci.c: Digital Devices microcode interface |
4 | * |
5 | * Copyright (C) 2017-2018 Digital Devices GmbH |
6 | * Ralph Metzler <rjkm@metzlerbros.de> |
7 | * Marcus Metzler <mocm@metzlerbros.de> |
8 | */ |
9 | |
10 | #include "ddbridge.h" |
11 | #include "ddbridge-io.h" |
12 | #include "ddbridge-mci.h" |
13 | |
14 | static LIST_HEAD(mci_list); |
15 | |
16 | static int mci_reset(struct mci *state) |
17 | { |
18 | struct ddb_link *link = state->base->link; |
19 | u32 status = 0; |
20 | u32 timeout = 40; |
21 | |
22 | ddblwritel(link, MCI_CONTROL_RESET, MCI_CONTROL); |
23 | ddblwritel(link, val: 0, MCI_CONTROL + 4); /* 1= no internal init */ |
24 | msleep(msecs: 300); |
25 | ddblwritel(link, val: 0, MCI_CONTROL); |
26 | |
27 | while (1) { |
28 | status = ddblreadl(link, MCI_CONTROL); |
29 | if ((status & MCI_CONTROL_READY) == MCI_CONTROL_READY) |
30 | break; |
31 | if (--timeout == 0) |
32 | break; |
33 | msleep(msecs: 50); |
34 | } |
35 | if ((status & MCI_CONTROL_READY) == 0) |
36 | return -1; |
37 | if (link->ids.device == 0x0009) |
38 | ddblwritel(link, SX8_TSCONFIG_MODE_NORMAL, SX8_TSCONFIG); |
39 | return 0; |
40 | } |
41 | |
42 | int ddb_mci_config(struct mci *state, u32 config) |
43 | { |
44 | struct ddb_link *link = state->base->link; |
45 | |
46 | if (link->ids.device != 0x0009) |
47 | return -EINVAL; |
48 | ddblwritel(link, val: config, SX8_TSCONFIG); |
49 | return 0; |
50 | } |
51 | |
52 | static int _mci_cmd_unlocked(struct mci *state, |
53 | u32 *cmd, u32 cmd_len, |
54 | u32 *res, u32 res_len) |
55 | { |
56 | struct ddb_link *link = state->base->link; |
57 | u32 i, val; |
58 | unsigned long stat; |
59 | |
60 | val = ddblreadl(link, MCI_CONTROL); |
61 | if (val & (MCI_CONTROL_RESET | MCI_CONTROL_START_COMMAND)) |
62 | return -EIO; |
63 | if (cmd && cmd_len) |
64 | for (i = 0; i < cmd_len; i++) |
65 | ddblwritel(link, val: cmd[i], MCI_COMMAND + i * 4); |
66 | val |= (MCI_CONTROL_START_COMMAND | MCI_CONTROL_ENABLE_DONE_INTERRUPT); |
67 | ddblwritel(link, val, MCI_CONTROL); |
68 | |
69 | stat = wait_for_completion_timeout(x: &state->base->completion, HZ); |
70 | if (stat == 0) { |
71 | dev_warn(state->base->dev, "MCI-%d: MCI timeout\n" , state->nr); |
72 | return -EIO; |
73 | } |
74 | if (res && res_len) |
75 | for (i = 0; i < res_len; i++) |
76 | res[i] = ddblreadl(link, MCI_RESULT + i * 4); |
77 | return 0; |
78 | } |
79 | |
80 | int ddb_mci_cmd(struct mci *state, |
81 | struct mci_command *command, |
82 | struct mci_result *result) |
83 | { |
84 | int stat; |
85 | |
86 | mutex_lock(&state->base->mci_lock); |
87 | stat = _mci_cmd_unlocked(state, |
88 | cmd: (u32 *)command, cmd_len: sizeof(*command) / sizeof(u32), |
89 | res: (u32 *)result, res_len: sizeof(*result) / sizeof(u32)); |
90 | mutex_unlock(lock: &state->base->mci_lock); |
91 | return stat; |
92 | } |
93 | |
94 | static void mci_handler(void *priv) |
95 | { |
96 | struct mci_base *base = (struct mci_base *)priv; |
97 | |
98 | complete(&base->completion); |
99 | } |
100 | |
101 | static struct mci_base *match_base(void *key) |
102 | { |
103 | struct mci_base *p; |
104 | |
105 | list_for_each_entry(p, &mci_list, mci_list) |
106 | if (p->key == key) |
107 | return p; |
108 | return NULL; |
109 | } |
110 | |
111 | static int probe(struct mci *state) |
112 | { |
113 | mci_reset(state); |
114 | return 0; |
115 | } |
116 | |
117 | struct dvb_frontend |
118 | *ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr, |
119 | int (**fn_set_input)(struct dvb_frontend *fe, int input)) |
120 | { |
121 | struct ddb_port *port = input->port; |
122 | struct ddb *dev = port->dev; |
123 | struct ddb_link *link = &dev->link[port->lnr]; |
124 | struct mci_base *base; |
125 | struct mci *state; |
126 | void *key = cfg->type ? (void *)port : (void *)link; |
127 | |
128 | state = kzalloc(size: cfg->state_size, GFP_KERNEL); |
129 | if (!state) |
130 | return NULL; |
131 | |
132 | base = match_base(key); |
133 | if (base) { |
134 | base->count++; |
135 | state->base = base; |
136 | } else { |
137 | base = kzalloc(size: cfg->base_size, GFP_KERNEL); |
138 | if (!base) |
139 | goto fail; |
140 | base->key = key; |
141 | base->count = 1; |
142 | base->link = link; |
143 | base->dev = dev->dev; |
144 | mutex_init(&base->mci_lock); |
145 | mutex_init(&base->tuner_lock); |
146 | ddb_irq_set(dev, link: link->nr, nr: 0, handler: mci_handler, data: base); |
147 | init_completion(x: &base->completion); |
148 | state->base = base; |
149 | if (probe(state) < 0) { |
150 | kfree(objp: base); |
151 | goto fail; |
152 | } |
153 | list_add(new: &base->mci_list, head: &mci_list); |
154 | if (cfg->base_init) |
155 | cfg->base_init(base); |
156 | } |
157 | memcpy(&state->fe.ops, cfg->fe_ops, sizeof(struct dvb_frontend_ops)); |
158 | state->fe.demodulator_priv = state; |
159 | state->nr = nr; |
160 | *fn_set_input = cfg->set_input; |
161 | state->tuner = nr; |
162 | state->demod = nr; |
163 | if (cfg->init) |
164 | cfg->init(state); |
165 | return &state->fe; |
166 | fail: |
167 | kfree(objp: state); |
168 | return NULL; |
169 | } |
170 | |