162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ddbridge-mci.c: Digital Devices microcode interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017-2018 Digital Devices GmbH 662306a36Sopenharmony_ci * Ralph Metzler <rjkm@metzlerbros.de> 762306a36Sopenharmony_ci * Marcus Metzler <mocm@metzlerbros.de> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "ddbridge.h" 1162306a36Sopenharmony_ci#include "ddbridge-io.h" 1262306a36Sopenharmony_ci#include "ddbridge-mci.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic LIST_HEAD(mci_list); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int mci_reset(struct mci *state) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct ddb_link *link = state->base->link; 1962306a36Sopenharmony_ci u32 status = 0; 2062306a36Sopenharmony_ci u32 timeout = 40; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci ddblwritel(link, MCI_CONTROL_RESET, MCI_CONTROL); 2362306a36Sopenharmony_ci ddblwritel(link, 0, MCI_CONTROL + 4); /* 1= no internal init */ 2462306a36Sopenharmony_ci msleep(300); 2562306a36Sopenharmony_ci ddblwritel(link, 0, MCI_CONTROL); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci while (1) { 2862306a36Sopenharmony_ci status = ddblreadl(link, MCI_CONTROL); 2962306a36Sopenharmony_ci if ((status & MCI_CONTROL_READY) == MCI_CONTROL_READY) 3062306a36Sopenharmony_ci break; 3162306a36Sopenharmony_ci if (--timeout == 0) 3262306a36Sopenharmony_ci break; 3362306a36Sopenharmony_ci msleep(50); 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci if ((status & MCI_CONTROL_READY) == 0) 3662306a36Sopenharmony_ci return -1; 3762306a36Sopenharmony_ci if (link->ids.device == 0x0009) 3862306a36Sopenharmony_ci ddblwritel(link, SX8_TSCONFIG_MODE_NORMAL, SX8_TSCONFIG); 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciint ddb_mci_config(struct mci *state, u32 config) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct ddb_link *link = state->base->link; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (link->ids.device != 0x0009) 4762306a36Sopenharmony_ci return -EINVAL; 4862306a36Sopenharmony_ci ddblwritel(link, config, SX8_TSCONFIG); 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int _mci_cmd_unlocked(struct mci *state, 5362306a36Sopenharmony_ci u32 *cmd, u32 cmd_len, 5462306a36Sopenharmony_ci u32 *res, u32 res_len) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct ddb_link *link = state->base->link; 5762306a36Sopenharmony_ci u32 i, val; 5862306a36Sopenharmony_ci unsigned long stat; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci val = ddblreadl(link, MCI_CONTROL); 6162306a36Sopenharmony_ci if (val & (MCI_CONTROL_RESET | MCI_CONTROL_START_COMMAND)) 6262306a36Sopenharmony_ci return -EIO; 6362306a36Sopenharmony_ci if (cmd && cmd_len) 6462306a36Sopenharmony_ci for (i = 0; i < cmd_len; i++) 6562306a36Sopenharmony_ci ddblwritel(link, cmd[i], MCI_COMMAND + i * 4); 6662306a36Sopenharmony_ci val |= (MCI_CONTROL_START_COMMAND | MCI_CONTROL_ENABLE_DONE_INTERRUPT); 6762306a36Sopenharmony_ci ddblwritel(link, val, MCI_CONTROL); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci stat = wait_for_completion_timeout(&state->base->completion, HZ); 7062306a36Sopenharmony_ci if (stat == 0) { 7162306a36Sopenharmony_ci dev_warn(state->base->dev, "MCI-%d: MCI timeout\n", state->nr); 7262306a36Sopenharmony_ci return -EIO; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci if (res && res_len) 7562306a36Sopenharmony_ci for (i = 0; i < res_len; i++) 7662306a36Sopenharmony_ci res[i] = ddblreadl(link, MCI_RESULT + i * 4); 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciint ddb_mci_cmd(struct mci *state, 8162306a36Sopenharmony_ci struct mci_command *command, 8262306a36Sopenharmony_ci struct mci_result *result) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci int stat; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci mutex_lock(&state->base->mci_lock); 8762306a36Sopenharmony_ci stat = _mci_cmd_unlocked(state, 8862306a36Sopenharmony_ci (u32 *)command, sizeof(*command) / sizeof(u32), 8962306a36Sopenharmony_ci (u32 *)result, sizeof(*result) / sizeof(u32)); 9062306a36Sopenharmony_ci mutex_unlock(&state->base->mci_lock); 9162306a36Sopenharmony_ci return stat; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void mci_handler(void *priv) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct mci_base *base = (struct mci_base *)priv; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci complete(&base->completion); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic struct mci_base *match_base(void *key) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct mci_base *p; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci list_for_each_entry(p, &mci_list, mci_list) 10662306a36Sopenharmony_ci if (p->key == key) 10762306a36Sopenharmony_ci return p; 10862306a36Sopenharmony_ci return NULL; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int probe(struct mci *state) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci mci_reset(state); 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct dvb_frontend 11862306a36Sopenharmony_ci*ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr, 11962306a36Sopenharmony_ci int (**fn_set_input)(struct dvb_frontend *fe, int input)) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct ddb_port *port = input->port; 12262306a36Sopenharmony_ci struct ddb *dev = port->dev; 12362306a36Sopenharmony_ci struct ddb_link *link = &dev->link[port->lnr]; 12462306a36Sopenharmony_ci struct mci_base *base; 12562306a36Sopenharmony_ci struct mci *state; 12662306a36Sopenharmony_ci void *key = cfg->type ? (void *)port : (void *)link; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci state = kzalloc(cfg->state_size, GFP_KERNEL); 12962306a36Sopenharmony_ci if (!state) 13062306a36Sopenharmony_ci return NULL; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci base = match_base(key); 13362306a36Sopenharmony_ci if (base) { 13462306a36Sopenharmony_ci base->count++; 13562306a36Sopenharmony_ci state->base = base; 13662306a36Sopenharmony_ci } else { 13762306a36Sopenharmony_ci base = kzalloc(cfg->base_size, GFP_KERNEL); 13862306a36Sopenharmony_ci if (!base) 13962306a36Sopenharmony_ci goto fail; 14062306a36Sopenharmony_ci base->key = key; 14162306a36Sopenharmony_ci base->count = 1; 14262306a36Sopenharmony_ci base->link = link; 14362306a36Sopenharmony_ci base->dev = dev->dev; 14462306a36Sopenharmony_ci mutex_init(&base->mci_lock); 14562306a36Sopenharmony_ci mutex_init(&base->tuner_lock); 14662306a36Sopenharmony_ci ddb_irq_set(dev, link->nr, 0, mci_handler, base); 14762306a36Sopenharmony_ci init_completion(&base->completion); 14862306a36Sopenharmony_ci state->base = base; 14962306a36Sopenharmony_ci if (probe(state) < 0) { 15062306a36Sopenharmony_ci kfree(base); 15162306a36Sopenharmony_ci goto fail; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci list_add(&base->mci_list, &mci_list); 15462306a36Sopenharmony_ci if (cfg->base_init) 15562306a36Sopenharmony_ci cfg->base_init(base); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci memcpy(&state->fe.ops, cfg->fe_ops, sizeof(struct dvb_frontend_ops)); 15862306a36Sopenharmony_ci state->fe.demodulator_priv = state; 15962306a36Sopenharmony_ci state->nr = nr; 16062306a36Sopenharmony_ci *fn_set_input = cfg->set_input; 16162306a36Sopenharmony_ci state->tuner = nr; 16262306a36Sopenharmony_ci state->demod = nr; 16362306a36Sopenharmony_ci if (cfg->init) 16462306a36Sopenharmony_ci cfg->init(state); 16562306a36Sopenharmony_ci return &state->fe; 16662306a36Sopenharmony_cifail: 16762306a36Sopenharmony_ci kfree(state); 16862306a36Sopenharmony_ci return NULL; 16962306a36Sopenharmony_ci} 170