/* * struct stm32_fmc2_prop - STM32 FMC2 EBI property * @name: the device tree binding name of the property * @bprop: indicate that it is a boolean property * @mprop: indicate that it is a mandatory property * @reg_type: the register that have to be modified * @reg_mask: the bit that have to be modified in the selected register * in case of it is a boolean property * @reset_val: the default value that have to be set in case the property * has not been defined in the device tree * @check: this callback ckecks that the property is compliant with the * transaction type selected * @calculate: this callback is called to calculate for exemple a timing * set in nanoseconds in the device tree in clock cycles or in * clock period * @set: this callback applies the values in the registers
*/ struct stm32_fmc2_prop { constchar *name; bool bprop; bool mprop; int reg_type;
u32 reg_mask;
u32 reset_val; int (*check)(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs);
u32 (*calculate)(struct stm32_fmc2_ebi *ebi, int cs, u32 setup); int (*set)(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs, u32 setup);
};
staticint stm32_fmc2_ebi_check_mux(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs)
{
u32 bcr; int ret;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if (bcr & FMC2_BCR_MTYP) return 0;
return -EINVAL;
}
staticint stm32_fmc2_ebi_check_waitcfg(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs)
{
u32 bcr, val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); int ret;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN) return 0;
return -EINVAL;
}
staticint stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs)
{
u32 bcr; int ret;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if (bcr & FMC2_BCR_BURSTEN) return 0;
return -EINVAL;
}
staticint stm32_fmc2_ebi_mp25_check_cclk(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs)
{ if (!ebi->access_granted) return -EACCES;
staticint stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs)
{
u32 bcr; int ret;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if (!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) return 0;
return -EINVAL;
}
staticint stm32_fmc2_ebi_check_cpsize(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs)
{
u32 bcr, val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM); int ret;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN) return 0;
return -EINVAL;
}
staticint stm32_fmc2_ebi_check_address_hold(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs)
{
u32 bcr, bxtr, val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D); int ret;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if (prop->reg_type == FMC2_REG_BWTR)
ret = regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr); else
ret = regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr); if (ret) return ret;
if ((!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) &&
((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN)) return 0;
return -EINVAL;
}
staticint stm32_fmc2_ebi_check_clk_period(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs)
{
u32 bcr, bcr1; int ret;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if (cs) {
ret = regmap_read(ebi->regmap, FMC2_BCR1, &bcr1); if (ret) return ret;
} else {
bcr1 = bcr;
}
switch (setup) { case FMC2_CPSIZE_0:
val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_0); break; case FMC2_CPSIZE_128:
val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_128); break; case FMC2_CPSIZE_256:
val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_256); break; case FMC2_CPSIZE_512:
val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_512); break; case FMC2_CPSIZE_1024:
val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_1024); break; default: /* Cpsize not supported */ return -EINVAL;
}
val = min_t(u32, setup, FMC2_BCR_NBLSET_MAX);
val = FIELD_PREP(FMC2_BCR_NBLSET, val);
regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_NBLSET, val);
return 0;
}
staticint stm32_fmc2_ebi_set_address_setup(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs, u32 setup)
{
u32 bcr, bxtr, reg;
u32 val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D); int ret;
ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); if (ret) return ret;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if (prop->reg_type == FMC2_REG_BWTR)
ret = regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr); else
ret = regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr); if (ret) return ret;
if ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN)
val = clamp_val(setup, 1, FMC2_BXTR_ADDSET_MAX); else
val = min_t(u32, setup, FMC2_BXTR_ADDSET_MAX);
val = FIELD_PREP(FMC2_BXTR_ADDSET, val);
regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_ADDSET, val);
return 0;
}
staticint stm32_fmc2_ebi_set_address_hold(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs, u32 setup)
{
u32 val, reg; int ret;
ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); if (ret) return ret;
val = clamp_val(setup, 1, FMC2_BXTR_ADDHLD_MAX);
val = FIELD_PREP(FMC2_BXTR_ADDHLD, val);
regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_ADDHLD, val);
return 0;
}
staticint stm32_fmc2_ebi_set_data_setup(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs, u32 setup)
{
u32 val, reg; int ret;
ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); if (ret) return ret;
val = clamp_val(setup, 1, FMC2_BXTR_DATAST_MAX);
val = FIELD_PREP(FMC2_BXTR_DATAST, val);
regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_DATAST, val);
return 0;
}
staticint stm32_fmc2_ebi_set_bus_turnaround(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs, u32 setup)
{
u32 val, reg; int ret;
ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); if (ret) return ret;
val = setup ? min_t(u32, setup - 1, FMC2_BXTR_BUSTURN_MAX) : 0;
val = FIELD_PREP(FMC2_BXTR_BUSTURN, val);
regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_BUSTURN, val);
return 0;
}
staticint stm32_fmc2_ebi_set_data_hold(struct stm32_fmc2_ebi *ebi, conststruct stm32_fmc2_prop *prop, int cs, u32 setup)
{
u32 val, reg; int ret;
ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, ®); if (ret) return ret;
if (prop->reg_type == FMC2_REG_BWTR)
val = setup ? min_t(u32, setup - 1, FMC2_BXTR_DATAHLD_MAX) : 0; else
val = min_t(u32, setup, FMC2_BXTR_DATAHLD_MAX);
val = FIELD_PREP(FMC2_BXTR_DATAHLD, val);
regmap_update_bits(ebi->regmap, reg, FMC2_BXTR_DATAHLD, val);
if (resource >= FMC2_MAX_RESOURCES) return -EINVAL;
ret = regmap_read(ebi->regmap, FMC2_SECCFGR, &seccfgr); if (ret) return ret;
if (seccfgr & BIT(resource)) { if (resource)
dev_err(ebi->dev, "resource %d is configured as secure\n",
resource);
return -EACCES;
}
ret = regmap_read(ebi->regmap, FMC2_CIDCFGR(resource), &cidcfgr); if (ret) return ret;
if (!(cidcfgr & FMC2_CIDCFGR_CFEN)) /* CID filtering is turned off: access granted */ return 0;
if (!(cidcfgr & FMC2_CIDCFGR_SEMEN)) { /* Static CID mode */
cid = FIELD_GET(FMC2_CIDCFGR_SCID, cidcfgr); if (cid != FMC2_CID1) { if (resource)
dev_err(ebi->dev, "static CID%d set for resource %d\n",
cid, resource);
return -EACCES;
}
return 0;
}
/* Pass-list with semaphore mode */ if (!(cidcfgr & FMC2_CIDCFGR_SEMWLC1)) { if (resource)
dev_err(ebi->dev, "CID1 is block-listed for resource %d\n",
resource);
return -EACCES;
}
ret = regmap_read(ebi->regmap, FMC2_SEMCR(resource), &semcr); if (ret) return ret;
if (!(semcr & FMC2_SEMCR_SEM_MUTEX)) {
regmap_update_bits(ebi->regmap, FMC2_SEMCR(resource),
FMC2_SEMCR_SEM_MUTEX, FMC2_SEMCR_SEM_MUTEX);
ret = regmap_read(ebi->regmap, FMC2_SEMCR(resource), &semcr); if (ret) return ret;
}
cid = FIELD_GET(FMC2_SEMCR_SEMCID, semcr); if (cid != FMC2_CID1) { if (resource)
dev_err(ebi->dev, "resource %d is already used by CID%d\n",
resource, cid);
if (!prop->set) {
dev_err(dev, "property %s is not well defined\n", prop->name); return -EINVAL;
}
if (prop->check && prop->check(ebi, prop, cs)) /* Skeep this property */ return 0;
if (prop->bprop) { bool bprop;
bprop = of_property_read_bool(dev_node, prop->name); if (prop->mprop && !bprop) {
dev_err(dev, "mandatory property %s not defined in the device tree\n",
prop->name); return -EINVAL;
}
if (bprop)
setup = 1;
} else {
u32 val; int ret;
ret = of_property_read_u32(dev_node, prop->name, &val); if (prop->mprop && ret) {
dev_err(dev, "mandatory property %s not defined in the device tree\n",
prop->name); return ret;
}
staticint stm32_fmc2_ebi_save_setup(struct stm32_fmc2_ebi *ebi)
{ unsignedint cs; int ret;
for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { if (!(ebi->bank_assigned & BIT(cs))) continue;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &ebi->bcr[cs]);
ret |= regmap_read(ebi->regmap, FMC2_BTR(cs), &ebi->btr[cs]);
ret |= regmap_read(ebi->regmap, FMC2_BWTR(cs), &ebi->bwtr[cs]); if (ret) return ret;
}
return 0;
}
staticint stm32_fmc2_ebi_mp1_save_setup(struct stm32_fmc2_ebi *ebi)
{ int ret;
ret = stm32_fmc2_ebi_save_setup(ebi); if (ret) return ret;
for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { if (!(ebi->bank_assigned & BIT(cs))) continue;
stm32_fmc2_ebi_disable_bank(ebi, cs);
}
}
/* NWAIT signal can not be connected to EBI controller and NAND controller */ staticint stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi)
{ struct device *dev = ebi->dev; unsignedint cs;
u32 bcr; int ret;
for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { if (!(ebi->bank_assigned & BIT(cs))) continue;
ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (ret) return ret;
if ((bcr & FMC2_BCR_WAITEN || bcr & FMC2_BCR_ASYNCWAIT) &&
ebi->bank_assigned & BIT(FMC2_NAND)) {
dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n"); return -EINVAL;
}
}
return 0;
}
staticvoid stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi)
{ if (!ebi->access_granted) return;
for (i = 0; i < ebi->data->nb_child_props; i++) { conststruct stm32_fmc2_prop *p = &ebi->data->child_props[i];
ret = stm32_fmc2_ebi_parse_prop(ebi, dev_node, p, cs); if (ret) {
dev_err(ebi->dev, "property %s could not be set: %d\n",
p->name, ret); return ret;
}
}
ebi = devm_kzalloc(&pdev->dev, sizeof(*ebi), GFP_KERNEL); if (!ebi) return -ENOMEM;
ebi->dev = dev;
platform_set_drvdata(pdev, ebi);
ebi->data = of_device_get_match_data(dev); if (!ebi->data) return -EINVAL;
ebi->regmap = device_node_to_regmap(dev->of_node); if (IS_ERR(ebi->regmap)) return PTR_ERR(ebi->regmap);
ebi->clk = devm_clk_get(dev, NULL); if (IS_ERR(ebi->clk)) return PTR_ERR(ebi->clk);
rstc = devm_reset_control_get(dev, NULL); if (PTR_ERR(rstc) == -EPROBE_DEFER) return -EPROBE_DEFER;
ret = devm_pm_runtime_enable(dev); if (ret) return ret;
ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret;
if (!IS_ERR(rstc)) {
reset_control_assert(rstc);
reset_control_deassert(rstc);
}
/* Check if CFGR register can be modified */
ebi->access_granted = true; if (ebi->data->check_rif) {
ret = ebi->data->check_rif(ebi, 0); if (ret) {
u32 sr;
ebi->access_granted = false;
ret = regmap_read(ebi->regmap, FMC2_SR, &sr); if (ret) goto err_release;
/* In case of CFGR is secure, just check that the FMC2 is enabled */ if (sr & FMC2_SR_ISOST) {
dev_err(dev, "FMC2 is not ready to be used.\n");
ret = -EACCES; goto err_release;
}
}
}
ret = stm32_fmc2_ebi_parse_dt(ebi); if (ret) goto err_release;
ret = ebi->data->save_setup(ebi); if (ret) goto err_release;
return 0;
err_release:
stm32_fmc2_ebi_disable_banks(ebi);
stm32_fmc2_ebi_disable(ebi); if (ebi->data->put_sems)
ebi->data->put_sems(ebi);
pm_runtime_put_sync_suspend(dev);
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.