170 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ddbridge-mci.c: Digital Devices microcode interface4 *5 * Copyright (C) 2017-2018 Digital Devices GmbH6 * 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 14static LIST_HEAD(mci_list);15 16static 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, 0, MCI_CONTROL + 4); /* 1= no internal init */24 msleep(300);25 ddblwritel(link, 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(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 42int 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, config, SX8_TSCONFIG);49 return 0;50}51 52static 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, 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(&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 80int 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 (u32 *)command, sizeof(*command) / sizeof(u32),89 (u32 *)result, sizeof(*result) / sizeof(u32));90 mutex_unlock(&state->base->mci_lock);91 return stat;92}93 94static void mci_handler(void *priv)95{96 struct mci_base *base = (struct mci_base *)priv;97 98 complete(&base->completion);99}100 101static 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 111static int probe(struct mci *state)112{113 mci_reset(state);114 return 0;115}116 117struct dvb_frontend118*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(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(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->nr, 0, mci_handler, base);147 init_completion(&base->completion);148 state->base = base;149 if (probe(state) < 0) {150 kfree(base);151 goto fail;152 }153 list_add(&base->mci_list, &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;166fail:167 kfree(state);168 return NULL;169}170