/** * si476x_core_config_pinmux() - pin function configuration function * * @core: Core device structure * * Configure the functions of the pins of the radio chip. * * The function returns zero in case of succes or negative error code * otherwise.
*/ staticint si476x_core_config_pinmux(struct si476x_core *core)
{ int err;
dev_dbg(&core->client->dev, "Configuring pinmux\n");
err = si476x_core_cmd_dig_audio_pin_cfg(core,
core->pinmux.dclk,
core->pinmux.dfs,
core->pinmux.dout,
core->pinmux.xout); if (err < 0) {
dev_err(&core->client->dev, "Failed to configure digital audio pins(err = %d)\n",
err); return err;
}
err = si476x_core_cmd_zif_pin_cfg(core,
core->pinmux.iqclk,
core->pinmux.iqfs,
core->pinmux.iout,
core->pinmux.qout); if (err < 0) {
dev_err(&core->client->dev, "Failed to configure ZIF pins(err = %d)\n",
err); return err;
}
/** * si476x_core_start() - early chip startup function * @core: Core device structure * @soft: When set, this flag forces "soft" startup, where "soft" * power down is the one done by sending appropriate command instead * of using reset pin of the tuner * * Perform required startup sequence to correctly power * up the chip and perform initial configuration. It does the * following sequence of actions: * 1. Claims and enables the power supplies VD and VIO1 required * for I2C interface of the chip operation. * 2. Waits for 100us, pulls the reset line up, enables irq, * waits for another 100us as it is specified by the * datasheet. * 3. Sends 'POWER_UP' command to the device with all provided * information about power-up parameters. * 4. Configures, pin multiplexor, disables digital audio and * configures interrupt sources. * * The function returns zero in case of succes or negative error code * otherwise.
*/ int si476x_core_start(struct si476x_core *core, bool soft)
{ struct i2c_client *client = core->client; int err;
if (!soft) { if (gpio_is_valid(core->gpio_reset))
gpio_set_value_cansleep(core->gpio_reset, 1);
if (client->irq)
enable_irq(client->irq);
udelay(100);
if (!client->irq) {
atomic_set(&core->is_alive, 1);
si476x_core_schedule_polling_work(core);
}
} else { if (client->irq)
enable_irq(client->irq); else {
atomic_set(&core->is_alive, 1);
si476x_core_schedule_polling_work(core);
}
}
/** * si476x_core_stop() - chip power-down function * @core: Core device structure * @soft: When set, function sends a POWER_DOWN command instead of * bringing reset line low * * Power down the chip by performing following actions: * 1. Disable IRQ or stop the polling worker * 2. Send the POWER_DOWN command if the power down is soft or bring * reset line low if not. * * The function returns zero in case of succes or negative error code * otherwise.
*/ int si476x_core_stop(struct si476x_core *core, bool soft)
{ int err = 0;
atomic_set(&core->is_alive, 0);
if (soft) { /* TODO: This probably shoud be a configurable option, * so it is possible to have the chips keep their * oscillators running
*/ struct si476x_power_down_args args = {
.xosc = false,
};
err = si476x_core_cmd_power_down(core, &args);
}
/* We couldn't disable those before * 'si476x_core_cmd_power_down' since we expect to get CTS
* interrupt */ if (core->client->irq)
disable_irq(core->client->irq); else
cancel_delayed_work_sync(&core->status_monitor);
if (!soft) { if (gpio_is_valid(core->gpio_reset))
gpio_set_value_cansleep(core->gpio_reset, 0);
} return err;
}
EXPORT_SYMBOL_GPL(si476x_core_stop);
/** * si476x_core_set_power_state() - set the level at which the power is * supplied for the chip. * @core: Core device structure * @next_state: enum si476x_power_state describing power state to * switch to. * * Switch on all the required power supplies * * This function returns 0 in case of suvccess and negative error code * otherwise.
*/ int si476x_core_set_power_state(struct si476x_core *core, enum si476x_power_state next_state)
{ /* It is not clear form the datasheet if it is possible to work with device if not all power domains are operational. So for now the power-up policy is "power-up all the things!"
*/ int err = 0;
if (core->power_state == SI476X_POWER_INCONSISTENT) {
dev_err(&core->client->dev, "The device in inconsistent power state\n"); return -EINVAL;
}
if (next_state != core->power_state) { switch (next_state) { case SI476X_POWER_UP_FULL:
err = regulator_bulk_enable(ARRAY_SIZE(core->supplies),
core->supplies); if (err < 0) {
core->power_state = SI476X_POWER_INCONSISTENT; break;
} /* * Startup timing diagram recommends to have a * 100 us delay between enabling of the power * supplies and turning the tuner on.
*/
udelay(100);
err = si476x_core_start(core, false); if (err < 0) goto disable_regulators;
/** * si476x_core_report_drainer_stop() - mark the completion of the RDS * buffer drain porcess by the worker. * * @core: Core device structure
*/ staticinlinevoid si476x_core_report_drainer_stop(struct si476x_core *core)
{
mutex_lock(&core->rds_drainer_status_lock);
core->rds_drainer_is_working = false;
mutex_unlock(&core->rds_drainer_status_lock);
}
/** * si476x_core_start_rds_drainer_once() - start RDS drainer worker if * ther is none working, do nothing otherwise * * @core: Datastructure corresponding to the chip.
*/ staticinlinevoid si476x_core_start_rds_drainer_once(struct si476x_core *core)
{
mutex_lock(&core->rds_drainer_status_lock); if (!core->rds_drainer_is_working) {
core->rds_drainer_is_working = true;
schedule_work(&core->rds_fifo_drainer);
}
mutex_unlock(&core->rds_drainer_status_lock);
} /** * si476x_core_drain_rds_fifo() - RDS buffer drainer. * @work: struct work_struct being ppassed to the function by the * kernel. * * Drain the contents of the RDS FIFO of
*/ staticvoid si476x_core_drain_rds_fifo(struct work_struct *work)
{ int err;
si476x_core_lock(core);
err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report); if (!err) { int i = report.rdsfifoused;
dev_dbg(&core->client->dev, "%d elements in RDS FIFO. Draining.\n", i); for (; i > 0; --i) {
err = si476x_core_cmd_fm_rds_status(core, false, false,
(i == 1), &report); if (err < 0) goto unlock;
/** * si476x_core_pronounce_dead() * * @core: Core device structure * * Mark the device as being dead and wake up all potentially waiting * threads of execution. *
*/ staticvoid si476x_core_pronounce_dead(struct si476x_core *core)
{
dev_info(&core->client->dev, "Core device is dead.\n");
atomic_set(&core->is_alive, 0);
/* Wake up al possible waiting processes */
wake_up_interruptible(&core->rds_read_queue);
/** * si476x_core_i2c_xfer() * * @core: Core device structure * @type: Transfer type * @buf: Transfer buffer for/with data * @count: Transfer buffer size * * Perfrom and I2C transfer(either read or write) and keep a counter * of I/O errors. If the error counter rises above the threshold * pronounce device dead. * * The function returns zero on succes or negative error code on * failure.
*/ int si476x_core_i2c_xfer(struct si476x_core *core, enum si476x_i2c_type type, char *buf, int count)
{ staticint io_errors_count; int err; if (type == SI476X_I2C_SEND)
err = i2c_master_send(core->client, buf, count); else
err = i2c_master_recv(core->client, buf, count);
if (err < 0) { if (io_errors_count++ > SI476X_MAX_IO_ERRORS)
si476x_core_pronounce_dead(core);
} else {
io_errors_count = 0;
}
/** * si476x_core_get_status() * @core: Core device structure * * Get the status byte of the core device by berforming one byte I2C * read. * * The function returns a status value or a negative error code on * error.
*/ staticint si476x_core_get_status(struct si476x_core *core)
{
u8 response; int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
&response, sizeof(response));
return (err < 0) ? err : response;
}
/** * si476x_core_get_and_signal_status() - IRQ dispatcher * @core: Core device structure * * Dispatch the arrived interrupt request based on the value of the * status byte reported by the tuner. *
*/ staticvoid si476x_core_get_and_signal_status(struct si476x_core *core)
{ int status = si476x_core_get_status(core); if (status < 0) {
dev_err(&core->client->dev, "Failed to get status\n"); return;
}
if (status & SI476X_CTS) { /* Unfortunately completions could not be used for * signalling CTS since this flag cannot be cleared * in status byte, and therefore once it becomes true * multiple calls to 'complete' would cause the * commands following the current one to be completed
* before they actually are */
dev_dbg(&core->client->dev, "[interrupt] CTSINT\n");
atomic_set(&core->cts, 1);
wake_up(&core->command);
}
if (status & SI476X_FM_RDS_INT) {
dev_dbg(&core->client->dev, "[interrupt] RDSINT\n");
si476x_core_start_rds_drainer_once(core);
}
/** * si476x_core_fwver_to_revision() * @core: Core device structure * @func: Selects the boot function of the device: * *_BOOTLOADER - Boot loader * *_FM_RECEIVER - FM receiver * *_AM_RECEIVER - AM receiver * *_WB_RECEIVER - Weatherband receiver * @major: Firmware major number * @minor1: Firmware first minor number * @minor2: Firmware second minor number * * Convert a chip's firmware version number into an offset that later * will be used to as offset in "vtable" of tuner functions * * This function returns a positive offset in case of success and a -1 * in case of failure.
*/ staticint si476x_core_fwver_to_revision(struct si476x_core *core, int func, int major, int minor1, int minor2)
{ switch (func) { case SI476X_FUNC_FM_RECEIVER: switch (major) { case 5: return SI476X_REVISION_A10; case 8: return SI476X_REVISION_A20; case 10: return SI476X_REVISION_A30; default: goto unknown_revision;
} case SI476X_FUNC_AM_RECEIVER: switch (major) { case 5: return SI476X_REVISION_A10; case 7: return SI476X_REVISION_A20; case 9: return SI476X_REVISION_A30; default: goto unknown_revision;
} case SI476X_FUNC_WB_RECEIVER: switch (major) { case 3: return SI476X_REVISION_A10; case 5: return SI476X_REVISION_A20; case 7: return SI476X_REVISION_A30; default: goto unknown_revision;
} case SI476X_FUNC_BOOTLOADER: default: /* FALLTHROUGH */
BUG(); return -1;
}
unknown_revision:
dev_err(&core->client->dev, "Unsupported version of the firmware: %d.%d.%d, " "reverting to A10 compatible functions\n",
major, minor1, minor2);
return SI476X_REVISION_A10;
}
/** * si476x_core_get_revision_info() * @core: Core device structure * * Get the firmware version number of the device. It is done in * following three steps: * 1. Power-up the device * 2. Send the 'FUNC_INFO' command * 3. Powering the device down. * * The function return zero on success and a negative error code on * failure.
*/ staticint si476x_core_get_revision_info(struct si476x_core *core)
{ int rval; struct si476x_func_info info;
si476x_core_lock(core);
rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL); if (rval < 0) gotoexit;
rval = si476x_core_cmd_func_info(core, &info); if (rval < 0) goto power_down;
rval = devm_regulator_bulk_get(&client->dev,
ARRAY_SIZE(core->supplies),
core->supplies); if (rval) {
dev_err(&client->dev, "Failed to get all of the regulators\n"); goto free_gpio;
}
core->rds_fifo_depth = 20;
} else {
INIT_DELAYED_WORK(&core->status_monitor,
si476x_core_poll_loop);
dev_info(&client->dev, "No IRQ number specified, will use polling\n");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.