/* Used by madera-i2c and madera-spi drivers */ constchar *madera_name_from_type(enum madera_type type)
{ switch (type) { case CS47L15: return"CS47L15"; case CS47L35: return"CS47L35"; case CS47L85: return"CS47L85"; case CS47L90: return"CS47L90"; case CS47L91: return"CS47L91"; case CS42L92: return"CS42L92"; case CS47L92: return"CS47L92"; case CS47L93: return"CS47L93"; case WM1840: return"WM1840"; default: return"Unknown";
}
}
EXPORT_SYMBOL_GPL(madera_name_from_type);
staticint madera_wait_for_boot_noack(struct madera *madera)
{
ktime_t timeout; unsignedint val = 0; int ret = 0;
/* * We can't use an interrupt as we need to runtime resume to do so, * so we poll the status bit. This won't race with the interrupt * handler because it will be blocked on runtime resume. * The chip could NAK a read request while it is booting so ignore * errors from regmap_read.
*/
timeout = ktime_add_us(ktime_get(), MADERA_BOOT_POLL_TIMEOUT_USEC);
regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_1, &val); while (!(val & MADERA_BOOT_DONE_STS1) &&
!ktime_after(ktime_get(), timeout)) {
usleep_range(MADERA_BOOT_POLL_INTERVAL_USEC / 2,
MADERA_BOOT_POLL_INTERVAL_USEC);
regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_1, &val);
}
if (!(val & MADERA_BOOT_DONE_STS1)) {
dev_err(madera->dev, "Polling BOOT_DONE_STS timed out\n");
ret = -ETIMEDOUT;
}
return ret;
}
staticint madera_wait_for_boot(struct madera *madera)
{ int ret = madera_wait_for_boot_noack(madera);
/* * BOOT_DONE defaults to unmasked on boot so we must ack it. * Do this even after a timeout to avoid interrupt storms.
*/
regmap_write(madera->regmap, MADERA_IRQ1_STATUS_1,
MADERA_BOOT_DONE_EINT1);
pm_runtime_mark_last_busy(madera->dev);
return ret;
}
staticint madera_soft_reset(struct madera *madera)
{ int ret;
ret = regmap_write(madera->regmap, MADERA_SOFTWARE_RESET, 0); if (ret != 0) {
dev_err(madera->dev, "Failed to soft reset device: %d\n", ret); return ret;
}
/* Allow time for internal clocks to startup after reset */
usleep_range(MADERA_RESET_MIN_US, MADERA_RESET_MAX_US);
return 0;
}
staticvoid madera_enable_hard_reset(struct madera *madera)
{ /* * There are many existing out-of-tree users of these codecs that we * can't break so preserve the expected behaviour of setting the line * low to assert reset.
*/
gpiod_set_raw_value_cansleep(madera->pdata.reset, 0);
}
reset = devm_gpiod_get_optional(madera->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(reset)) return dev_err_probe(madera->dev, PTR_ERR(reset), "Failed to request /RESET");
/* * A hard reset is needed for full reset of the chip. We allow running * without hard reset only because it can be useful for early * prototyping and some debugging, but we need to warn it's not ideal.
*/ if (!reset)
dev_warn(madera->dev, "Running without reset GPIO is not recommended\n");
madera->pdata.reset = reset;
return 0;
}
staticvoid madera_set_micbias_info(struct madera *madera)
{ /* * num_childbias is an array because future codecs can have different * childbiases for each micbias. Unspecified values default to 0.
*/ switch (madera->type) { case CS47L15:
madera->num_micbias = 1;
madera->num_childbias[0] = 3; return; case CS47L35:
madera->num_micbias = 2;
madera->num_childbias[0] = 2;
madera->num_childbias[1] = 2; return; case CS47L85: case WM1840:
madera->num_micbias = 4; /* no child biases */ return; case CS47L90: case CS47L91:
madera->num_micbias = 2;
madera->num_childbias[0] = 4;
madera->num_childbias[1] = 4; return; case CS42L92: case CS47L92: case CS47L93:
madera->num_micbias = 2;
madera->num_childbias[0] = 4;
madera->num_childbias[1] = 2; return; default: return;
}
}
int madera_dev_init(struct madera *madera)
{ struct device *dev = madera->dev; unsignedint hwid; int (*patch_fn)(struct madera *) = NULL; conststruct mfd_cell *mfd_devs = NULL; int n_devs = 0; int i, ret;
/* * We need writable hw config info that all children can share. * Simplest to take one shared copy of pdata struct.
*/ if (dev_get_platdata(madera->dev)) {
memcpy(&madera->pdata, dev_get_platdata(madera->dev), sizeof(madera->pdata));
}
ret = devm_clk_bulk_get_optional(madera->dev, ARRAY_SIZE(madera->mclk),
madera->mclk); if (ret) {
dev_err(madera->dev, "Failed to get clocks: %d\n", ret); return ret;
}
/* Not using devm_clk_get to prevent breakage of existing DTs */ if (!madera->mclk[MADERA_MCLK2].clk)
dev_warn(madera->dev, "Missing MCLK2, requires 32kHz clock\n");
ret = madera_get_reset_gpio(madera); if (ret) return ret;
/* * On some codecs DCVDD could be supplied by the internal LDO1. * For those we must add the LDO1 driver before requesting DCVDD * No devm_ because we need to control shutdown order of children.
*/ switch (madera->type) { case CS47L15:
madera->reset_errata = true; break; case CS47L35: case CS47L90: case CS47L91: case CS42L92: case CS47L92: case CS47L93: break; case CS47L85: case WM1840:
ret = mfd_add_devices(madera->dev, PLATFORM_DEVID_NONE,
madera_ldo1_devs,
ARRAY_SIZE(madera_ldo1_devs),
NULL, 0, NULL); if (ret) {
dev_err(dev, "Failed to add LDO1 child: %d\n", ret); return ret;
} break; default: /* No point continuing if the type is unknown */
dev_err(madera->dev, "Unknown device type %d\n", madera->type); return -ENODEV;
}
ret = devm_regulator_bulk_get(dev, madera->num_core_supplies,
madera->core_supplies); if (ret) {
dev_err(dev, "Failed to request core supplies: %d\n", ret); goto err_devs;
}
/* * Don't use devres here. If the regulator is one of our children it * will already have been removed before devres cleanup on this mfd * driver tries to call put() on it. We need control of shutdown order.
*/
madera->dcvdd = regulator_get(madera->dev, "DCVDD"); if (IS_ERR(madera->dcvdd)) {
ret = PTR_ERR(madera->dcvdd);
dev_err(dev, "Failed to request DCVDD: %d\n", ret); goto err_devs;
}
ret = regulator_bulk_enable(madera->num_core_supplies,
madera->core_supplies); if (ret) {
dev_err(dev, "Failed to enable core supplies: %d\n", ret); goto err_dcvdd;
}
if (madera->reset_errata)
madera_disable_hard_reset(madera);
ret = regulator_enable(madera->dcvdd); if (ret) {
dev_err(dev, "Failed to enable DCVDD: %d\n", ret); goto err_enable;
}
if (madera->reset_errata)
usleep_range(ERRATA_DCVDD_MIN_US, ERRATA_DCVDD_MAX_US); else
madera_disable_hard_reset(madera);
ret = madera_wait_for_boot_noack(madera); if (ret) {
dev_err(madera->dev, "Device failed initial boot: %d\n", ret); goto err_reset;
}
/* * Now we can power up and verify that this is a chip we know about * before we start doing any writes to its registers.
*/
ret = regmap_read(madera->regmap, MADERA_SOFTWARE_RESET, &hwid); if (ret) {
dev_err(dev, "Failed to read ID register: %d\n", ret); goto err_reset;
}
switch (hwid) { case CS47L15_SILICON_ID: if (IS_ENABLED(CONFIG_MFD_CS47L15)) { switch (madera->type) { case CS47L15:
patch_fn = &cs47l15_patch;
mfd_devs = cs47l15_devs;
n_devs = ARRAY_SIZE(cs47l15_devs); break; default: break;
}
} break; case CS47L35_SILICON_ID: if (IS_ENABLED(CONFIG_MFD_CS47L35)) { switch (madera->type) { case CS47L35:
patch_fn = cs47l35_patch;
mfd_devs = cs47l35_devs;
n_devs = ARRAY_SIZE(cs47l35_devs); break; default: break;
}
} break; case CS47L85_SILICON_ID: if (IS_ENABLED(CONFIG_MFD_CS47L85)) { switch (madera->type) { case CS47L85: case WM1840:
patch_fn = cs47l85_patch;
mfd_devs = cs47l85_devs;
n_devs = ARRAY_SIZE(cs47l85_devs); break; default: break;
}
} break; case CS47L90_SILICON_ID: if (IS_ENABLED(CONFIG_MFD_CS47L90)) { switch (madera->type) { case CS47L90: case CS47L91:
patch_fn = cs47l90_patch;
mfd_devs = cs47l90_devs;
n_devs = ARRAY_SIZE(cs47l90_devs); break; default: break;
}
} break; case CS47L92_SILICON_ID: if (IS_ENABLED(CONFIG_MFD_CS47L92)) { switch (madera->type) { case CS42L92: case CS47L92: case CS47L93:
patch_fn = cs47l92_patch;
mfd_devs = cs47l92_devs;
n_devs = ARRAY_SIZE(cs47l92_devs); break; default: break;
}
} break; default:
dev_err(madera->dev, "Unknown device ID: %x\n", hwid);
ret = -EINVAL; goto err_reset;
}
if (!n_devs || !mfd_devs) {
dev_err(madera->dev, "Device ID 0x%x not a %s\n", hwid,
madera->type_name);
ret = -ENODEV; goto err_reset;
}
/* * It looks like a device we support. If we don't have a hard reset * we can now attempt a soft reset.
*/ if (!madera->pdata.reset || madera->reset_errata) {
ret = madera_soft_reset(madera); if (ret) goto err_reset;
}
ret = madera_wait_for_boot(madera); if (ret) {
dev_err(madera->dev, "Failed to clear boot done: %d\n", ret); goto err_reset;
}
ret = regmap_read(madera->regmap, MADERA_HARDWARE_REVISION,
&madera->rev); if (ret) {
dev_err(dev, "Failed to read revision register: %d\n", ret); goto err_reset;
}
madera->rev &= MADERA_HW_REVISION_MASK;
/* No devm_ because we need to control shutdown order of children */
ret = mfd_add_devices(madera->dev, PLATFORM_DEVID_NONE,
mfd_devs, n_devs,
NULL, 0, NULL); if (ret) {
dev_err(madera->dev, "Failed to add subdevices: %d\n", ret); goto err_pm_runtime;
}
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.