diff options
Diffstat (limited to 'drivers')
218 files changed, 10136 insertions, 1611 deletions
diff --git a/drivers/amba/tegra-ahb.c b/drivers/amba/tegra-ahb.c index 57d3b2e2d007..0b2c20fddb7c 100644 --- a/drivers/amba/tegra-ahb.c +++ b/drivers/amba/tegra-ahb.c @@ -120,7 +120,7 @@ static const u32 tegra_ahb_gizmo[] = { struct tegra_ahb { void __iomem *regs; struct device *dev; - u32 ctx[0]; + u32 ctx[]; }; static inline u32 gizmo_readl(struct tegra_ahb *ahb, u32 offset) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 69361ec43db5..b1cd4d97bc2a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -42,7 +42,6 @@ #include <linux/workqueue.h> #include <linux/scatterlist.h> #include <linux/io.h> -#include <linux/async.h> #include <linux/log2.h> #include <linux/slab.h> #include <linux/glob.h> @@ -5778,7 +5777,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) /* perform each probe asynchronously */ for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - async_schedule(async_port_probe, ap); + ap->cookie = async_schedule(async_port_probe, ap); } return 0; @@ -5920,11 +5919,11 @@ void ata_host_detach(struct ata_host *host) { int i; - /* Ensure ata_port probe has completed */ - async_synchronize_full(); - - for (i = 0; i < host->n_ports; i++) + for (i = 0; i < host->n_ports; i++) { + /* Ensure ata_port probe has completed */ + async_synchronize_cookie(host->ports[i]->cookie + 1); ata_port_detach(host->ports[i]); + } /* the host is dead now, dissociate ACPI */ ata_acpi_dissociate(host); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 435781a16875..46336084b1a9 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3684,12 +3684,13 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) { struct scsi_cmnd *scmd = qc->scsicmd; const u8 *cdb = scmd->cmnd; - const u8 *p; u8 pg, spg; unsigned six_byte, pg_len, hdr_len, bd_len; int len; u16 fp = (u16)-1; u8 bp = 0xff; + u8 buffer[64]; + const u8 *p = buffer; VPRINTK("ENTER\n"); @@ -3723,12 +3724,14 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc) if (!scsi_sg_count(scmd) || scsi_sglist(scmd)->length < len) goto invalid_param_len; - p = page_address(sg_page(scsi_sglist(scmd))); - /* Move past header and block descriptors. */ if (len < hdr_len) goto invalid_param_len; + if (!sg_copy_to_buffer(scsi_sglist(scmd), scsi_sg_count(scmd), + buffer, sizeof(buffer))) + goto invalid_param_len; + if (six_byte) bd_len = p[3]; else diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index 980aacdbcf3b..141ac600b64c 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -907,7 +907,7 @@ static int sata_rcar_probe(struct platform_device *pdev) pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) - goto err_pm_disable; + goto err_pm_put; host = ata_host_alloc(dev, 1); if (!host) { @@ -937,7 +937,6 @@ static int sata_rcar_probe(struct platform_device *pdev) err_pm_put: pm_runtime_put(dev); -err_pm_disable: pm_runtime_disable(dev); return ret; } @@ -991,8 +990,10 @@ static int sata_rcar_resume(struct device *dev) int ret; ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put(dev); return ret; + } if (priv->type == RCAR_GEN3_SATA) { sata_rcar_init_module(priv); @@ -1017,8 +1018,10 @@ static int sata_rcar_restore(struct device *dev) int ret; ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put(dev); return ret; + } sata_rcar_setup_port(host); diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 14345a87c7cc..33d0831c99b6 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -620,7 +620,7 @@ struct fifo_buffer { unsigned int head_index; unsigned int size; int total; /* sum of all values */ - int values[0]; + int values[]; }; extern struct fifo_buffer *fifo_alloc(unsigned int fifo_size); diff --git a/drivers/block/drbd/drbd_protocol.h b/drivers/block/drbd/drbd_protocol.h index e6fc5ad72501..dea59c92ecc1 100644 --- a/drivers/block/drbd/drbd_protocol.h +++ b/drivers/block/drbd/drbd_protocol.h @@ -271,7 +271,7 @@ struct p_rs_param { u32 resync_rate; /* Since protocol version 88 and higher. */ - char verify_alg[0]; + char verify_alg[]; } __packed; struct p_rs_param_89 { @@ -305,7 +305,7 @@ struct p_protocol { u32 two_primaries; /* Since protocol version 87 and higher. */ - char integrity_alg[0]; + char integrity_alg[]; } __packed; @@ -360,7 +360,7 @@ struct p_sizes { u16 dds_flags; /* use enum dds_flags here. */ /* optional queue_limits if (agreed_features & DRBD_FF_WSAME) */ - struct o_qlim qlim[0]; + struct o_qlim qlim[]; } __packed; struct p_state { @@ -409,7 +409,7 @@ struct p_compressed_bm { */ u8 encoding; - u8 code[0]; + u8 code[]; } __packed; struct p_delay_probe93 { diff --git a/drivers/block/loop.c b/drivers/block/loop.c index c33bbbfd1bd9..475e1a738560 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1368,14 +1368,14 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) lo->lo_sizelimit != info->lo_sizelimit) { size_changed = true; sync_blockdev(lo->lo_device); - kill_bdev(lo->lo_device); + invalidate_bdev(lo->lo_device); } /* I/O need to be drained during transfer transition */ blk_mq_freeze_queue(lo->lo_queue); if (size_changed && lo->lo_device->bd_inode->i_mapping->nrpages) { - /* If any pages were dirtied after kill_bdev(), try again */ + /* If any pages were dirtied after invalidate_bdev(), try again */ err = -EAGAIN; pr_warn("%s: loop%d (%s) has still dirty pages (nrpages=%lu)\n", __func__, lo->lo_number, lo->lo_file_name, @@ -1615,11 +1615,11 @@ static int loop_set_block_size(struct loop_device *lo, unsigned long arg) return 0; sync_blockdev(lo->lo_device); - kill_bdev(lo->lo_device); + invalidate_bdev(lo->lo_device); blk_mq_freeze_queue(lo->lo_queue); - /* kill_bdev should have truncated all the pages */ + /* invalidate_bdev should have truncated all the pages */ if (lo->lo_device->bd_inode->i_mapping->nrpages) { err = -EAGAIN; pr_warn("%s: loop%d (%s) has still dirty pages (nrpages=%lu)\n", diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 7420648a1de6..4f61e9209461 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1451,8 +1451,10 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req) static void rbd_osd_format_read(struct ceph_osd_request *osd_req) { struct rbd_obj_request *obj_request = osd_req->r_priv; + struct rbd_device *rbd_dev = obj_request->img_request->rbd_dev; + struct ceph_options *opt = rbd_dev->rbd_client->client->options; - osd_req->r_flags = CEPH_OSD_FLAG_READ; + osd_req->r_flags = CEPH_OSD_FLAG_READ | opt->read_from_replica; osd_req->r_snapid = obj_request->img_request->snap_id; } diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c index e2330e757f1f..001617033d6a 100644 --- a/drivers/char/hw_random/ks-sa-rng.c +++ b/drivers/char/hw_random/ks-sa-rng.c @@ -244,6 +244,7 @@ static int ks_sa_rng_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(dev); if (ret < 0) { dev_err(dev, "Failed to enable SA power-domain\n"); + pm_runtime_put_noidle(dev); pm_runtime_disable(dev); return ret; } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 31cae88a730b..934c92dcb9ab 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -171,7 +171,7 @@ static ssize_t read_mem(struct file *file, char __user *buf, if (!ptr) goto failed; - probe = probe_kernel_read(bounce, ptr, sz); + probe = copy_from_kernel_nofault(bounce, ptr, sz); unxlate_dev_mem_ptr(p, ptr); if (probe) goto failed; diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index 2491a2cb54a2..2ccdf8ac6994 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -214,11 +214,10 @@ static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data) * Return: * On success, 0. On failure, -errno. */ -static int ftpm_tee_probe(struct platform_device *pdev) +static int ftpm_tee_probe(struct device *dev) { int rc; struct tpm_chip *chip; - struct device *dev = &pdev->dev; struct ftpm_tee_private *pvt_data = NULL; struct tee_ioctl_open_session_arg sess_arg; @@ -297,6 +296,13 @@ out_tee_session: return rc; } +static int ftpm_plat_tee_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + return ftpm_tee_probe(dev); +} + /** * ftpm_tee_remove() - remove the TPM device * @pdev: the platform_device description. @@ -304,9 +310,9 @@ out_tee_session: * Return: * 0 always. */ -static int ftpm_tee_remove(struct platform_device *pdev) +static int ftpm_tee_remove(struct device *dev) { - struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev); + struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev); /* Release the chip */ tpm_chip_unregister(pvt_data->chip); @@ -328,11 +334,18 @@ static int ftpm_tee_remove(struct platform_device *pdev) return 0; } +static int ftpm_plat_tee_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + return ftpm_tee_remove(dev); +} + /** * ftpm_tee_shutdown() - shutdown the TPM device * @pdev: the platform_device description. */ -static void ftpm_tee_shutdown(struct platform_device *pdev) +static void ftpm_plat_tee_shutdown(struct platform_device *pdev) { struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev); @@ -347,17 +360,54 @@ static const struct of_device_id of_ftpm_tee_ids[] = { }; MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids); -static struct platform_driver ftpm_tee_driver = { +static struct platform_driver ftpm_tee_plat_driver = { .driver = { .name = "ftpm-tee", .of_match_table = of_match_ptr(of_ftpm_tee_ids), }, - .probe = ftpm_tee_probe, - .remove = ftpm_tee_remove, - .shutdown = ftpm_tee_shutdown, + .shutdown = ftpm_plat_tee_shutdown, + .probe = ftpm_plat_tee_probe, + .remove = ftpm_plat_tee_remove, +}; + +/* UUID of the fTPM TA */ +static const struct tee_client_device_id optee_ftpm_id_table[] = { + {UUID_INIT(0xbc50d971, 0xd4c9, 0x42c4, + 0x82, 0xcb, 0x34, 0x3f, 0xb7, 0xf3, 0x78, 0x96)}, + {} }; -module_platform_driver(ftpm_tee_driver); +MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table); + +static struct tee_client_driver ftpm_tee_driver = { + .id_table = optee_ftpm_id_table, + .driver = { + .name = "optee-ftpm", + .bus = &tee_bus_type, + .probe = ftpm_tee_probe, + .remove = ftpm_tee_remove, + }, +}; + +static int __init ftpm_mod_init(void) +{ + int rc; + + rc = platform_driver_register(&ftpm_tee_plat_driver); + if (rc) + return rc; + + return driver_register(&ftpm_tee_driver.driver); +} + +static void __exit ftpm_mod_exit(void) +{ + platform_driver_unregister(&ftpm_tee_plat_driver); + driver_unregister(&ftpm_tee_driver.driver); +} + +module_init(ftpm_mod_init); +module_exit(ftpm_mod_exit); MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>"); MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE"); diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index c491f5de0f3f..c754dfbb73fd 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -103,6 +103,8 @@ static const struct clk_ops scmi_clk_ops = { static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk) { int ret; + unsigned long min_rate, max_rate; + struct clk_init_data init = { .flags = CLK_GET_RATE_NOCACHE, .num_parents = 0, @@ -112,9 +114,23 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk) sclk->hw.init = &init; ret = devm_clk_hw_register(dev, &sclk->hw); - if (!ret) - clk_hw_set_rate_range(&sclk->hw, sclk->info->range.min_rate, - sclk->info->range.max_rate); + if (ret) + return ret; + + if (sclk->info->rate_discrete) { + int num_rates = sclk->info->list.num_rates; + + if (num_rates <= 0) + return -EINVAL; + + min_rate = sclk->info->list.rates[0]; + max_rate = sclk->info->list.rates[num_rates - 1]; + } else { + min_rate = sclk->info->range.min_rate; + max_rate = sclk->info->range.max_rate; + } + + clk_hw_set_rate_range(&sclk->hw, min_rate, max_rate); return ret; } diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 61623e2ff149..1cf688fcb56b 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -198,7 +198,8 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = latency; - policy->fast_switch_possible = true; + policy->fast_switch_possible = + handle->perf_ops->fast_switch_possible(handle, cpu_dev); em_register_perf_domain(policy->cpus, nr_opp, &em_cb); diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig index a62f228be6da..bc35aa0ec07a 100644 --- a/drivers/crypto/caam/Kconfig +++ b/drivers/crypto/caam/Kconfig @@ -147,7 +147,7 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API select HW_RANDOM help Selecting this will register the SEC4 hardware rng to - the hw_random API for suppying the kernel entropy pool. + the hw_random API for supplying the kernel entropy pool. endif # CRYPTO_DEV_FSL_CAAM_JR diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 4fcdd262e581..f3d20b7645e0 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -54,7 +54,7 @@ static void build_instantiation_desc(u32 *desc, int handle, int do_sk) /* * load 1 to clear written reg: - * resets the done interrrupt and returns the RNG to idle. + * resets the done interrupt and returns the RNG to idle. */ append_load_imm_u32(desc, 1, LDST_SRCDST_WORD_CLRW); @@ -156,7 +156,7 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc, DESC_DER_DECO_STAT_SHIFT; /* - * If an error occured in the descriptor, then + * If an error occurred in the descriptor, then * the DECO status field will be set to 0x0D */ if (deco_state == DECO_STAT_HOST_ERR) @@ -264,7 +264,7 @@ static void devm_deinstantiate_rng(void *data) * - -ENODEV if DECO0 couldn't be acquired * - -EAGAIN if an error occurred when executing the descriptor * f.i. there was a RNG hardware error due to not "good enough" - * entropy being aquired. + * entropy being acquired. */ static int instantiate_rng(struct device *ctrldev, int state_handle_mask, int gen_sk) @@ -733,8 +733,8 @@ static int caam_probe(struct platform_device *pdev) handle_imx6_err005766(&ctrl->mcr); /* - * Read the Compile Time paramters and SCFGR to determine - * if Virtualization is enabled for this platform + * Read the Compile Time parameters and SCFGR to determine + * if virtualization is enabled for this platform */ scfgr = rd_reg32(&ctrl->scfgr); @@ -863,9 +863,9 @@ static int caam_probe(struct platform_device *pdev) } /* * if instantiate_rng(...) fails, the loop will rerun - * and the kick_trng(...) function will modfiy the + * and the kick_trng(...) function will modify the * upper and lower limits of the entropy sampling - * interval, leading to a sucessful initialization of + * interval, leading to a successful initialization of * the RNG. */ ret = instantiate_rng(dev, inst_handles, @@ -882,8 +882,8 @@ static int caam_probe(struct platform_device *pdev) return ret; } /* - * Set handles init'ed by this module as the complement of the - * already initialized ones + * Set handles initialized by this module as the complement of + * the already initialized ones */ ctrlpriv->rng4_sh_init = ~ctrlpriv->rng4_sh_init & RDSTA_MASK; diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h index e796d3cb9be8..e13470901586 100644 --- a/drivers/crypto/caam/desc.h +++ b/drivers/crypto/caam/desc.h @@ -18,7 +18,7 @@ */ #define SEC4_SG_LEN_EXT 0x80000000 /* Entry points to table */ -#define SEC4_SG_LEN_FIN 0x40000000 /* Last ent in table */ +#define SEC4_SG_LEN_FIN 0x40000000 /* Last entry in table */ #define SEC4_SG_BPID_MASK 0x000000ff #define SEC4_SG_BPID_SHIFT 16 #define SEC4_SG_LEN_MASK 0x3fffffff /* Excludes EXT and FINAL */ @@ -113,7 +113,7 @@ */ #define HDR_REVERSE 0x00000800 -/* Propogate DNR property to SharedDesc */ +/* Propagate DNR property to SharedDesc */ #define HDR_PROP_DNR 0x00000800 /* JobDesc/SharedDesc share property */ diff --git a/drivers/crypto/caam/pdb.h b/drivers/crypto/caam/pdb.h index 68c1fd5dee5d..8ccc22075043 100644 --- a/drivers/crypto/caam/pdb.h +++ b/drivers/crypto/caam/pdb.h @@ -453,7 +453,7 @@ struct srtp_decap_pdb { #define DSA_PDB_N_MASK 0x7f struct dsa_sign_pdb { - u32 sgf_ln; /* Use DSA_PDB_ defintions per above */ + u32 sgf_ln; /* Use DSA_PDB_ definitions per above */ u8 *q; u8 *r; u8 *g; /* or Gx,y */ diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index a2426334be61..476113e12489 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -376,6 +376,7 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable) struct sev_device *sev = psp_master->sev_data; struct sev_user_data_pek_csr input; struct sev_data_pek_csr *data; + void __user *input_address; void *blob = NULL; int ret; @@ -394,6 +395,7 @@ static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp, bool writable) goto cmd; /* allocate a physically contiguous buffer to store the CSR blob */ + input_address = (void __user *)input.address; if (input.length > SEV_FW_BLOB_MAX_SIZE) { ret = -EFAULT; goto e_free; @@ -426,7 +428,7 @@ cmd: } if (blob) { - if (copy_to_user((void __user *)input.address, blob, input.length)) + if (copy_to_user(input_address, blob, input.length)) ret = -EFAULT; } @@ -437,7 +439,7 @@ e_free: return ret; } -void *psp_copy_user_blob(u64 __user uaddr, u32 len) +void *psp_copy_user_blob(u64 uaddr, u32 len) { if (!uaddr || !len) return ERR_PTR(-EINVAL); @@ -446,7 +448,7 @@ void *psp_copy_user_blob(u64 __user uaddr, u32 len) if (len > SEV_FW_BLOB_MAX_SIZE) return ERR_PTR(-EINVAL); - return memdup_user((void __user *)(uintptr_t)uaddr, len); + return memdup_user((void __user *)uaddr, len); } EXPORT_SYMBOL_GPL(psp_copy_user_blob); @@ -621,6 +623,7 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp) { struct sev_user_data_get_id2 input; struct sev_data_get_id *data; + void __user *input_address; void *id_blob = NULL; int ret; @@ -631,6 +634,8 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp) if (copy_from_user(&input, (void __user *)argp->data, sizeof(input))) return -EFAULT; + input_address = (void __user *)input.address; + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -660,8 +665,7 @@ static int sev_ioctl_do_get_id2(struct sev_issue_cmd *argp) } if (id_blob) { - if (copy_to_user((void __user *)input.address, - id_blob, data->len)) { + if (copy_to_user(input_address, id_blob, data->len)) { ret = -EFAULT; goto e_free; } @@ -720,6 +724,8 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) struct sev_user_data_pdh_cert_export input; void *pdh_blob = NULL, *cert_blob = NULL; struct sev_data_pdh_cert_export *data; + void __user *input_cert_chain_address; + void __user *input_pdh_cert_address; int ret; /* If platform is not in INIT state then transition it to INIT. */ @@ -745,6 +751,9 @@ static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp, bool writable) !input.cert_chain_address) goto cmd; + input_pdh_cert_address = (void __user *)input.pdh_cert_address; + input_cert_chain_address = (void __user *)input.cert_chain_address; + /* Allocate a physically contiguous buffer to store the PDH blob. */ if (input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) { ret = -EFAULT; @@ -788,7 +797,7 @@ cmd: } if (pdh_blob) { - if (copy_to_user((void __user *)input.pdh_cert_address, + if (copy_to_user(input_pdh_cert_address, pdh_blob, input.pdh_cert_len)) { ret = -EFAULT; goto e_free_cert; @@ -796,7 +805,7 @@ cmd: } if (cert_blob) { - if (copy_to_user((void __user *)input.cert_chain_address, + if (copy_to_user(input_cert_chain_address, cert_blob, input.cert_chain_len)) ret = -EFAULT; } diff --git a/drivers/crypto/chelsio/chcr_crypto.h b/drivers/crypto/chelsio/chcr_crypto.h index b3fdbdc25acb..31e427e273f8 100644 --- a/drivers/crypto/chelsio/chcr_crypto.h +++ b/drivers/crypto/chelsio/chcr_crypto.h @@ -223,7 +223,7 @@ struct chcr_authenc_ctx { struct __aead_ctx { struct chcr_gcm_ctx gcm[0]; - struct chcr_authenc_ctx authenc[0]; + struct chcr_authenc_ctx authenc[]; }; struct chcr_aead_ctx { @@ -235,7 +235,7 @@ struct chcr_aead_ctx { u8 nonce[4]; u16 hmac_ctrl; u16 mayverify; - struct __aead_ctx ctx[0]; + struct __aead_ctx ctx[]; }; struct hmac_ctx { @@ -247,7 +247,7 @@ struct hmac_ctx { struct __crypto_ctx { struct hmac_ctx hmacctx[0]; struct ablk_ctx ablkctx[0]; - struct chcr_aead_ctx aeadctx[0]; + struct chcr_aead_ctx aeadctx[]; }; struct chcr_context { @@ -257,7 +257,7 @@ struct chcr_context { unsigned int ntxq; unsigned int nrxq; struct completion cbc_aes_aio_done; - struct __crypto_ctx crypto_ctx[0]; + struct __crypto_ctx crypto_ctx[]; }; struct chcr_hctx_per_wr { diff --git a/drivers/crypto/hisilicon/sgl.c b/drivers/crypto/hisilicon/sgl.c index 0e8c7e324fb4..725a739800b0 100644 --- a/drivers/crypto/hisilicon/sgl.c +++ b/drivers/crypto/hisilicon/sgl.c @@ -66,7 +66,8 @@ struct hisi_acc_sgl_pool *hisi_acc_create_sgl_pool(struct device *dev, sgl_size = sizeof(struct acc_hw_sge) * sge_nr + sizeof(struct hisi_acc_hw_sgl); - block_size = PAGE_SIZE * (1 << (MAX_ORDER - 1)); + block_size = 1 << (PAGE_SHIFT + MAX_ORDER <= 32 ? + PAGE_SHIFT + MAX_ORDER - 1 : 31); sgl_num_per_block = block_size / sgl_size; block_num = count / sgl_num_per_block; remain_sgl = count % sgl_num_per_block; diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_algs.c b/drivers/crypto/marvell/octeontx/otx_cptvf_algs.c index 60e744f680d3..1e0a1d70ebd3 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_algs.c +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_algs.c @@ -118,6 +118,9 @@ static void otx_cpt_aead_callback(int status, void *arg1, void *arg2) struct otx_cpt_req_info *cpt_req; struct pci_dev *pdev; + if (!cpt_info) + goto complete; + cpt_req = cpt_info->req; if (!status) { /* @@ -129,10 +132,10 @@ static void otx_cpt_aead_callback(int status, void *arg1, void *arg2) !cpt_req->is_enc) status = validate_hmac_cipher_null(cpt_req); } - if (cpt_info) { - pdev = cpt_info->pdev; - do_request_cleanup(pdev, cpt_info); - } + pdev = cpt_info->pdev; + do_request_cleanup(pdev, cpt_info); + +complete: if (areq) areq->complete(areq, status); } diff --git a/drivers/dio/dio.c b/drivers/dio/dio.c index c9aa15fb86a9..193b40e7aec0 100644 --- a/drivers/dio/dio.c +++ b/drivers/dio/dio.c @@ -135,7 +135,8 @@ int __init dio_find(int deviceid) else va = ioremap(pa, PAGE_SIZE); - if (probe_kernel_read(&i, (unsigned char *)va + DIO_IDOFF, 1)) { + if (copy_from_kernel_nofault(&i, + (unsigned char *)va + DIO_IDOFF, 1)) { if (scode >= DIOII_SCBASE) iounmap(va); continue; /* no board present at that select code */ @@ -208,7 +209,8 @@ static int __init dio_init(void) else va = ioremap(pa, PAGE_SIZE); - if (probe_kernel_read(&i, (unsigned char *)va + DIO_IDOFF, 1)) { + if (copy_from_kernel_nofault(&i, + (unsigned char *)va + DIO_IDOFF, 1)) { if (scode >= DIOII_SCBASE) iounmap(va); continue; /* no board present at that select code */ diff --git a/drivers/dma/milbeaut-hdmac.c b/drivers/dma/milbeaut-hdmac.c index 8853d442430b..a8cfb59f6efe 100644 --- a/drivers/dma/milbeaut-hdmac.c +++ b/drivers/dma/milbeaut-hdmac.c @@ -77,7 +77,7 @@ struct milbeaut_hdmac_device { struct dma_device ddev; struct clk *clk; void __iomem *reg_base; - struct milbeaut_hdmac_chan channels[0]; + struct milbeaut_hdmac_chan channels[]; }; static struct milbeaut_hdmac_chan * diff --git a/drivers/dma/milbeaut-xdmac.c b/drivers/dma/milbeaut-xdmac.c index ab3d2f395378..85a597228fb0 100644 --- a/drivers/dma/milbeaut-xdmac.c +++ b/drivers/dma/milbeaut-xdmac.c @@ -74,7 +74,7 @@ struct milbeaut_xdmac_chan { struct milbeaut_xdmac_device { struct dma_device ddev; void __iomem *reg_base; - struct milbeaut_xdmac_chan channels[0]; + struct milbeaut_xdmac_chan channels[]; }; static struct milbeaut_xdmac_chan * diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c index 4ab493d46375..347146a6e1d0 100644 --- a/drivers/dma/moxart-dma.c +++ b/drivers/dma/moxart-dma.c @@ -127,7 +127,7 @@ struct moxart_desc { unsigned int dma_cycles; struct virt_dma_desc vd; uint8_t es; - struct moxart_sg sg[0]; + struct moxart_sg sg[]; }; struct moxart_chan { diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index b9f0d9636620..55fc7400f717 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -225,7 +225,7 @@ struct tegra_dma { u32 global_pause_count; /* Last member of the structure */ - struct tegra_dma_channel channels[0]; + struct tegra_dma_channel channels[]; }; static inline void tdma_write(struct tegra_dma *tdma, u32 reg, u32 val) diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c index c4a5c170c1f9..35d81bd857f1 100644 --- a/drivers/dma/ti/edma.c +++ b/drivers/dma/ti/edma.c @@ -211,7 +211,7 @@ struct edma_desc { u32 residue; u32 residue_stat; - struct edma_pset pset[0]; + struct edma_pset pset[]; }; struct edma_cc; diff --git a/drivers/dma/ti/k3-udma-glue.c b/drivers/dma/ti/k3-udma-glue.c index 64c8955e0cf1..c888ae4fec96 100644 --- a/drivers/dma/ti/k3-udma-glue.c +++ b/drivers/dma/ti/k3-udma-glue.c @@ -271,20 +271,12 @@ struct k3_udma_glue_tx_channel *k3_udma_glue_request_tx_chn(struct device *dev, atomic_set(&tx_chn->free_pkts, cfg->txcq_cfg.size); /* request and cfg rings */ - tx_chn->ringtx = k3_ringacc_request_ring(tx_chn->common.ringacc, - tx_chn->udma_tchan_id, 0); - if (!tx_chn->ringtx) { - ret = -ENODEV; - dev_err(dev, "Failed to get TX ring %u\n", - tx_chn->udma_tchan_id); - goto err; - } - - tx_chn->ringtxcq = k3_ringacc_request_ring(tx_chn->common.ringacc, - -1, 0); - if (!tx_chn->ringtxcq) { - ret = -ENODEV; - dev_err(dev, "Failed to get TXCQ ring\n"); + ret = k3_ringacc_request_rings_pair(tx_chn->common.ringacc, + tx_chn->udma_tchan_id, -1, + &tx_chn->ringtx, + &tx_chn->ringtxcq); + if (ret) { + dev_err(dev, "Failed to get TX/TXCQ rings %d\n", ret); goto err; } @@ -587,22 +579,16 @@ static int k3_udma_glue_cfg_rx_flow(struct k3_udma_glue_rx_channel *rx_chn, } /* request and cfg rings */ - flow->ringrx = k3_ringacc_request_ring(rx_chn->common.ringacc, - flow_cfg->ring_rxq_id, 0); - if (!flow->ringrx) { - ret = -ENODEV; - dev_err(dev, "Failed to get RX ring\n"); + ret = k3_ringacc_request_rings_pair(rx_chn->common.ringacc, + flow_cfg->ring_rxq_id, + flow_cfg->ring_rxfdq0_id, + &flow->ringrxfdq, + &flow->ringrx); + if (ret) { + dev_err(dev, "Failed to get RX/RXFDQ rings %d\n", ret); goto err_rflow_put; } - flow->ringrxfdq = k3_ringacc_request_ring(rx_chn->common.ringacc, - flow_cfg->ring_rxfdq0_id, 0); - if (!flow->ringrxfdq) { - ret = -ENODEV; - dev_err(dev, "Failed to get RXFDQ ring\n"); - goto err_ringrx_free; - } - ret = k3_ringacc_ring_cfg(flow->ringrx, &flow_cfg->rx_cfg); if (ret) { dev_err(dev, "Failed to cfg ringrx %d\n", ret); @@ -673,8 +659,6 @@ static int k3_udma_glue_cfg_rx_flow(struct k3_udma_glue_rx_channel *rx_chn, err_ringrxfdq_free: k3_ringacc_ring_free(flow->ringrxfdq); - -err_ringrx_free: k3_ringacc_ring_free(flow->ringrx); err_rflow_put: diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 945b7c604f91..a389aa6d260f 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -170,7 +170,7 @@ struct udma_desc { void *metadata; /* pointer to provided metadata buffer (EPIP, PSdata) */ unsigned int hwdesc_count; - struct udma_hwdesc hwdesc[0]; + struct udma_hwdesc hwdesc[]; }; enum udma_chan_state { @@ -1418,17 +1418,12 @@ static int udma_alloc_tx_resources(struct udma_chan *uc) if (ret) return ret; - uc->tchan->t_ring = k3_ringacc_request_ring(ud->ringacc, - uc->tchan->id, 0); - if (!uc->tchan->t_ring) { - ret = -EBUSY; - goto err_tx_ring; - } - - uc->tchan->tc_ring = k3_ringacc_request_ring(ud->ringacc, -1, 0); - if (!uc->tchan->tc_ring) { + ret = k3_ringacc_request_rings_pair(ud->ringacc, uc->tchan->id, -1, + &uc->tchan->t_ring, + &uc->tchan->tc_ring); + if (ret) { ret = -EBUSY; - goto err_txc_ring; + goto err_ring; } memset(&ring_cfg, 0, sizeof(ring_cfg)); @@ -1447,10 +1442,9 @@ static int udma_alloc_tx_resources(struct udma_chan *uc) err_ringcfg: k3_ringacc_ring_free(uc->tchan->tc_ring); uc->tchan->tc_ring = NULL; -err_txc_ring: k3_ringacc_ring_free(uc->tchan->t_ring); uc->tchan->t_ring = NULL; -err_tx_ring: +err_ring: udma_put_tchan(uc); return ret; @@ -1499,16 +1493,11 @@ static int udma_alloc_rx_resources(struct udma_chan *uc) rflow = uc->rflow; fd_ring_id = ud->tchan_cnt + ud->echan_cnt + uc->rchan->id; - rflow->fd_ring = k3_ringacc_request_ring(ud->ringacc, fd_ring_id, 0); - if (!rflow->fd_ring) { - ret = -EBUSY; - goto err_rx_ring; - } - - rflow->r_ring = k3_ringacc_request_ring(ud->ringacc, -1, 0); - if (!rflow->r_ring) { + ret = k3_ringacc_request_rings_pair(ud->ringacc, fd_ring_id, -1, + &rflow->fd_ring, &rflow->r_ring); + if (ret) { ret = -EBUSY; - goto err_rxc_ring; + goto err_ring; } memset(&ring_cfg, 0, sizeof(ring_cfg)); @@ -1533,10 +1522,9 @@ static int udma_alloc_rx_resources(struct udma_chan *uc) err_ringcfg: k3_ringacc_ring_free(rflow->r_ring); rflow->r_ring = NULL; -err_rxc_ring: k3_ringacc_ring_free(rflow->fd_ring); rflow->fd_ring = NULL; -err_rx_ring: +err_ring: udma_put_rflow(uc); err_rflow: udma_put_rchan(uc); diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 39382694fdfc..68e48bf54d78 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -88,7 +88,7 @@ struct timb_dma { struct dma_device dma; void __iomem *membase; struct tasklet_struct tasklet; - struct timb_dma_chan channels[0]; + struct timb_dma_chan channels[]; }; static struct device *chan2dev(struct dma_chan *chan) diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index c7ea4f2d5ca6..fb6c651214f3 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -117,7 +117,7 @@ struct inbound_transaction_resource { struct descriptor_resource { struct client_resource resource; struct fw_descriptor descriptor; - u32 data[0]; + u32 data[]; }; struct iso_resource { diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index 404a035f104d..439d918bbaaf 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -620,7 +620,7 @@ struct fw_request { u32 request_header[4]; int ack; u32 length; - u32 data[0]; + u32 data[]; }; static void free_response_callback(struct fw_packet *packet, diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 4b0e4ee655a1..71d5f16f311c 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -191,7 +191,7 @@ struct fw_node { /* Upper layer specific data. */ void *data; - struct fw_node *ports[0]; + struct fw_node *ports[]; }; static inline struct fw_node *fw_node_get(struct fw_node *node) diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 6ca2f5ab6c57..5fd6a60b6741 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -52,7 +52,7 @@ struct pcl { struct packet { unsigned int length; - char data[0]; + char data[]; }; struct packet_buffer { diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 33269316f111..54fdc39cd0bc 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -111,7 +111,7 @@ struct descriptor_buffer { dma_addr_t buffer_bus; size_t buffer_size; size_t used; - struct descriptor buffer[0]; + struct descriptor buffer[]; }; struct context { diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 1cad32b38b29..6f9cbc4aef22 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o scmi-bus-y = bus.o -scmi-driver-y = driver.o +scmi-driver-y = driver.o notify.o scmi-transport-y = shmem.o scmi-transport-$(CONFIG_MAILBOX) += mailbox.o -scmi-transport-$(CONFIG_ARM_PSCI_FW) += smc.o +scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index ce7d9203e41b..9853bd3c4d45 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -5,7 +5,15 @@ * Copyright (C) 2018 ARM Ltd. */ +#define pr_fmt(fmt) "SCMI Notifications BASE - " fmt + +#include <linux/scmi_protocol.h> + #include "common.h" +#include "notify.h" + +#define SCMI_BASE_NUM_SOURCES 1 +#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024 enum scmi_base_protocol_cmd { BASE_DISCOVER_VENDOR = 0x3, @@ -19,16 +27,25 @@ enum scmi_base_protocol_cmd { BASE_RESET_AGENT_CONFIGURATION = 0xb, }; -enum scmi_base_protocol_notify { - BASE_ERROR_EVENT = 0x0, -}; - struct scmi_msg_resp_base_attributes { u8 num_protocols; u8 num_agents; __le16 reserved; }; +struct scmi_msg_base_error_notify { + __le32 event_control; +#define BASE_TP_NOTIFY_ALL BIT(0) +}; + +struct scmi_base_error_notify_payld { + __le32 agent_id; + __le32 error_status; +#define IS_FATAL_ERROR(x) ((x) & BIT(31)) +#define ERROR_CMD_COUNT(x) FIELD_GET(GENMASK(9, 0), (x)) + __le64 msg_reports[SCMI_BASE_MAX_CMD_ERR_COUNT]; +}; + /** * scmi_base_attributes_get() - gets the implementation details * that are associated with the base protocol. @@ -222,6 +239,83 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle, return ret; } +static int scmi_base_error_notify(const struct scmi_handle *handle, bool enable) +{ + int ret; + u32 evt_cntl = enable ? BASE_TP_NOTIFY_ALL : 0; + struct scmi_xfer *t; + struct scmi_msg_base_error_notify *cfg; + + ret = scmi_xfer_get_init(handle, BASE_NOTIFY_ERRORS, + SCMI_PROTOCOL_BASE, sizeof(*cfg), 0, &t); + if (ret) + return ret; + + cfg = t->tx.buf; + cfg->event_control = cpu_to_le32(evt_cntl); + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_base_set_notify_enabled(const struct scmi_handle *handle, + u8 evt_id, u32 src_id, bool enable) +{ + int ret; + + ret = scmi_base_error_notify(handle, enable); + if (ret) + pr_debug("FAIL_ENABLED - evt[%X] ret:%d\n", evt_id, ret); + + return ret; +} + +static void *scmi_base_fill_custom_report(const struct scmi_handle *handle, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id) +{ + int i; + const struct scmi_base_error_notify_payld *p = payld; + struct scmi_base_error_report *r = report; + + /* + * BaseError notification payload is variable in size but + * up to a maximum length determined by the struct ponted by p. + * Instead payld_sz is the effective length of this notification + * payload so cannot be greater of the maximum allowed size as + * pointed by p. + */ + if (evt_id != SCMI_EVENT_BASE_ERROR_EVENT || sizeof(*p) < payld_sz) + return NULL; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->fatal = IS_FATAL_ERROR(le32_to_cpu(p->error_status)); + r->cmd_count = ERROR_CMD_COUNT(le32_to_cpu(p->error_status)); + for (i = 0; i < r->cmd_count; i++) + r->reports[i] = le64_to_cpu(p->msg_reports[i]); + *src_id = 0; + + return r; +} + +static const struct scmi_event base_events[] = { + { + .id = SCMI_EVENT_BASE_ERROR_EVENT, + .max_payld_sz = sizeof(struct scmi_base_error_notify_payld), + .max_report_sz = sizeof(struct scmi_base_error_report) + + SCMI_BASE_MAX_CMD_ERR_COUNT * sizeof(u64), + }, +}; + +static const struct scmi_event_ops base_event_ops = { + .set_notify_enabled = scmi_base_set_notify_enabled, + .fill_custom_report = scmi_base_fill_custom_report, +}; + int scmi_base_protocol_init(struct scmi_handle *h) { int id, ret; @@ -256,6 +350,12 @@ int scmi_base_protocol_init(struct scmi_handle *h) dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols, rev->num_agents); + scmi_register_protocol_events(handle, SCMI_PROTOCOL_BASE, + (4 * SCMI_PROTO_QUEUE_SZ), + &base_event_ops, base_events, + ARRAY_SIZE(base_events), + SCMI_BASE_NUM_SOURCES); + for (id = 0; id < rev->num_agents; id++) { scmi_base_discover_agent_get(handle, id, name); dev_dbg(dev, "Agent %d: %s\n", id, name); diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 4c2227662b26..75e39882746e 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -5,6 +5,8 @@ * Copyright (C) 2018 ARM Ltd. */ +#include <linux/sort.h> + #include "common.h" enum scmi_clock_protocol_cmd { @@ -121,11 +123,23 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle, return ret; } +static int rate_cmp_func(const void *_r1, const void *_r2) +{ + const u64 *r1 = _r1, *r2 = _r2; + + if (*r1 < *r2) + return -1; + else if (*r1 == *r2) + return 0; + else + return 1; +} + static int scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id, struct scmi_clock_info *clk) { - u64 *rate; + u64 *rate = NULL; int ret, cnt; bool rate_discrete = false; u32 tot_rate_cnt = 0, rates_flag; @@ -184,8 +198,10 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id, */ } while (num_returned && num_remaining); - if (rate_discrete) + if (rate_discrete && rate) { clk->list.num_rates = tot_rate_cnt; + sort(rate, tot_rate_cnt, sizeof(*rate), rate_cmp_func, NULL); + } clk->rate_discrete = rate_discrete; diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 31fe5a22a011..c113e578cc6c 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -6,6 +6,8 @@ * * Copyright (C) 2018 ARM Ltd. */ +#ifndef _SCMI_COMMON_H +#define _SCMI_COMMON_H #include <linux/bitfield.h> #include <linux/completion.h> @@ -235,3 +237,5 @@ void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem); bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, struct scmi_xfer *xfer); + +#endif /* _SCMI_COMMON_H */ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 7483cacf63f9..03ec74242c14 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include "common.h" +#include "notify.h" #define CREATE_TRACE_POINTS #include <trace/events/scmi.h> @@ -208,7 +209,9 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr) struct device *dev = cinfo->dev; struct scmi_info *info = handle_to_scmi_info(cinfo->handle); struct scmi_xfers_info *minfo = &info->rx_minfo; + ktime_t ts; + ts = ktime_get_boottime(); xfer = scmi_xfer_get(cinfo->handle, minfo); if (IS_ERR(xfer)) { dev_err(dev, "failed to get free message slot (%ld)\n", @@ -221,6 +224,8 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr) scmi_dump_header_dbg(dev, &xfer->hdr); info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size, xfer); + scmi_notify(cinfo->handle, xfer->hdr.protocol_id, + xfer->hdr.id, xfer->rx.buf, xfer->rx.len, ts); trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id, xfer->hdr.protocol_id, xfer->hdr.seq, @@ -392,8 +397,7 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) info->desc->ops->mark_txdone(cinfo, ret); trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id, - xfer->hdr.protocol_id, xfer->hdr.seq, - xfer->hdr.status); + xfer->hdr.protocol_id, xfer->hdr.seq, ret); return ret; } @@ -789,6 +793,9 @@ static int scmi_probe(struct platform_device *pdev) if (ret) return ret; + if (scmi_notification_init(handle)) + dev_err(dev, "SCMI Notifications NOT available.\n"); + ret = scmi_base_protocol_init(handle); if (ret) { dev_err(dev, "unable to communicate with SCMI(%d)\n", ret); @@ -831,6 +838,8 @@ static int scmi_remove(struct platform_device *pdev) struct scmi_info *info = platform_get_drvdata(pdev); struct idr *idr = &info->tx_idr; + scmi_notification_exit(&info->handle); + mutex_lock(&scmi_list_mutex); if (info->users) ret = -EBUSY; @@ -901,7 +910,7 @@ ATTRIBUTE_GROUPS(versions); /* Each compatible listed below must have descriptor associated with it */ static const struct of_device_id scmi_of_match[] = { { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, -#ifdef CONFIG_ARM_PSCI_FW +#ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc}, #endif { /* Sentinel */ }, diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c new file mode 100644 index 000000000000..4731daaacd19 --- /dev/null +++ b/drivers/firmware/arm_scmi/notify.c @@ -0,0 +1,1526 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Notification support + * + * Copyright (C) 2020 ARM Ltd. + */ +/** + * DOC: Theory of operation + * + * SCMI Protocol specification allows the platform to signal events to + * interested agents via notification messages: this is an implementation + * of the dispatch and delivery of such notifications to the interested users + * inside the Linux kernel. + * + * An SCMI Notification core instance is initialized for each active platform + * instance identified by the means of the usual &struct scmi_handle. + * + * Each SCMI Protocol implementation, during its initialization, registers with + * this core its set of supported events using scmi_register_protocol_events(): + * all the needed descriptors are stored in the &struct registered_protocols and + * &struct registered_events arrays. + * + * Kernel users interested in some specific event can register their callbacks + * providing the usual notifier_block descriptor, since this core implements + * events' delivery using the standard Kernel notification chains machinery. + * + * Given the number of possible events defined by SCMI and the extensibility + * of the SCMI Protocol itself, the underlying notification chains are created + * and destroyed dynamically on demand depending on the number of users + * effectively registered for an event, so that no support structures or chains + * are allocated until at least one user has registered a notifier_block for + * such event. Similarly, events' generation itself is enabled at the platform + * level only after at least one user has registered, and it is shutdown after + * the last user for that event has gone. + * + * All users provided callbacks and allocated notification-chains are stored in + * the @registered_events_handlers hashtable. Callbacks' registration requests + * for still to be registered events are instead kept in the dedicated common + * hashtable @pending_events_handlers. + * + * An event is identified univocally by the tuple (proto_id, evt_id, src_id) + * and is served by its own dedicated notification chain; information contained + * in such tuples is used, in a few different ways, to generate the needed + * hash-keys. + * + * Here proto_id and evt_id are simply the protocol_id and message_id numbers + * as described in the SCMI Protocol specification, while src_id represents an + * optional, protocol dependent, source identifier (like domain_id, perf_id + * or sensor_id and so forth). + * + * Upon reception of a notification message from the platform the SCMI RX ISR + * passes the received message payload and some ancillary information (including + * an arrival timestamp in nanoseconds) to the core via @scmi_notify() which + * pushes the event-data itself on a protocol-dedicated kfifo queue for further + * deferred processing as specified in @scmi_events_dispatcher(). + * + * Each protocol has it own dedicated work_struct and worker which, once kicked + * by the ISR, takes care to empty its own dedicated queue, deliverying the + * queued items into the proper notification-chain: notifications processing can + * proceed concurrently on distinct workers only between events belonging to + * different protocols while delivery of events within the same protocol is + * still strictly sequentially ordered by time of arrival. + * + * Events' information is then extracted from the SCMI Notification messages and + * conveyed, converted into a custom per-event report struct, as the void *data + * param to the user callback provided by the registered notifier_block, so that + * from the user perspective his callback will look invoked like: + * + * int user_cb(struct notifier_block *nb, unsigned long event_id, void *report) + * + */ + +#define dev_fmt(fmt) "SCMI Notifications - " fmt +#define pr_fmt(fmt) "SCMI Notifications - " fmt + +#include <linux/bitfield.h> +#include <linux/bug.h> +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/hashtable.h> +#include <linux/kernel.h> +#include <linux/ktime.h> +#include <linux/kfifo.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/refcount.h> +#include <linux/scmi_protocol.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include "notify.h" + +#define SCMI_MAX_PROTO 256 + +#define PROTO_ID_MASK GENMASK(31, 24) +#define EVT_ID_MASK GENMASK(23, 16) +#define SRC_ID_MASK GENMASK(15, 0) + +/* + * Builds an unsigned 32bit key from the given input tuple to be used + * as a key in hashtables. + */ +#define MAKE_HASH_KEY(p, e, s) \ + (FIELD_PREP(PROTO_ID_MASK, (p)) | \ + FIELD_PREP(EVT_ID_MASK, (e)) | \ + FIELD_PREP(SRC_ID_MASK, (s))) + +#define MAKE_ALL_SRCS_KEY(p, e) MAKE_HASH_KEY((p), (e), SRC_ID_MASK) + +/* + * Assumes that the stored obj includes its own hash-key in a field named 'key': + * with this simplification this macro can be equally used for all the objects' + * types hashed by this implementation. + * + * @__ht: The hashtable name + * @__obj: A pointer to the object type to be retrieved from the hashtable; + * it will be used as a cursor while scanning the hastable and it will + * be possibly left as NULL when @__k is not found + * @__k: The key to search for + */ +#define KEY_FIND(__ht, __obj, __k) \ +({ \ + typeof(__k) k_ = __k; \ + typeof(__obj) obj_; \ + \ + hash_for_each_possible((__ht), obj_, hash, k_) \ + if (obj_->key == k_) \ + break; \ + __obj = obj_; \ +}) + +#define KEY_XTRACT_PROTO_ID(key) FIELD_GET(PROTO_ID_MASK, (key)) +#define KEY_XTRACT_EVT_ID(key) FIELD_GET(EVT_ID_MASK, (key)) +#define KEY_XTRACT_SRC_ID(key) FIELD_GET(SRC_ID_MASK, (key)) + +/* + * A set of macros used to access safely @registered_protocols and + * @registered_events arrays; these are fixed in size and each entry is possibly + * populated at protocols' registration time and then only read but NEVER + * modified or removed. + */ +#define SCMI_GET_PROTO(__ni, __pid) \ +({ \ + typeof(__ni) ni_ = __ni; \ + struct scmi_registered_events_desc *__pd = NULL; \ + \ + if (ni_) \ + __pd = READ_ONCE(ni_->registered_protocols[(__pid)]); \ + __pd; \ +}) + +#define SCMI_GET_REVT_FROM_PD(__pd, __eid) \ +({ \ + typeof(__pd) pd_ = __pd; \ + typeof(__eid) eid_ = __eid; \ + struct scmi_registered_event *__revt = NULL; \ + \ + if (pd_ && eid_ < pd_->num_events) \ + __revt = READ_ONCE(pd_->registered_events[eid_]); \ + __revt; \ +}) + +#define SCMI_GET_REVT(__ni, __pid, __eid) \ +({ \ + struct scmi_registered_event *__revt; \ + struct scmi_registered_events_desc *__pd; \ + \ + __pd = SCMI_GET_PROTO((__ni), (__pid)); \ + __revt = SCMI_GET_REVT_FROM_PD(__pd, (__eid)); \ + __revt; \ +}) + +/* A couple of utility macros to limit cruft when calling protocols' helpers */ +#define REVT_NOTIFY_SET_STATUS(revt, eid, sid, state) \ +({ \ + typeof(revt) r = revt; \ + r->proto->ops->set_notify_enabled(r->proto->ni->handle, \ + (eid), (sid), (state)); \ +}) + +#define REVT_NOTIFY_ENABLE(revt, eid, sid) \ + REVT_NOTIFY_SET_STATUS((revt), (eid), (sid), true) + +#define REVT_NOTIFY_DISABLE(revt, eid, sid) \ + REVT_NOTIFY_SET_STATUS((revt), (eid), (sid), false) + +#define REVT_FILL_REPORT(revt, ...) \ +({ \ + typeof(revt) r = revt; \ + r->proto->ops->fill_custom_report(r->proto->ni->handle, \ + __VA_ARGS__); \ +}) + +#define SCMI_PENDING_HASH_SZ 4 +#define SCMI_REGISTERED_HASH_SZ 6 + +struct scmi_registered_events_desc; + +/** + * struct scmi_notify_instance - Represents an instance of the notification + * core + * @gid: GroupID used for devres + * @handle: A reference to the platform instance + * @init_work: A work item to perform final initializations of pending handlers + * @notify_wq: A reference to the allocated Kernel cmwq + * @pending_mtx: A mutex to protect @pending_events_handlers + * @registered_protocols: A statically allocated array containing pointers to + * all the registered protocol-level specific information + * related to events' handling + * @pending_events_handlers: An hashtable containing all pending events' + * handlers descriptors + * + * Each platform instance, represented by a handle, has its own instance of + * the notification subsystem represented by this structure. + */ +struct scmi_notify_instance { + void *gid; + struct scmi_handle *handle; + struct work_struct init_work; + struct workqueue_struct *notify_wq; + /* lock to protect pending_events_handlers */ + struct mutex pending_mtx; + struct scmi_registered_events_desc **registered_protocols; + DECLARE_HASHTABLE(pending_events_handlers, SCMI_PENDING_HASH_SZ); +}; + +/** + * struct events_queue - Describes a queue and its associated worker + * @sz: Size in bytes of the related kfifo + * @kfifo: A dedicated Kernel kfifo descriptor + * @notify_work: A custom work item bound to this queue + * @wq: A reference to the associated workqueue + * + * Each protocol has its own dedicated events_queue descriptor. + */ +struct events_queue { + size_t sz; + struct kfifo kfifo; + struct work_struct notify_work; + struct workqueue_struct *wq; +}; + +/** + * struct scmi_event_header - A utility header + * @timestamp: The timestamp, in nanoseconds (boottime), which was associated + * to this event as soon as it entered the SCMI RX ISR + * @payld_sz: Effective size of the embedded message payload which follows + * @evt_id: Event ID (corresponds to the Event MsgID for this Protocol) + * @payld: A reference to the embedded event payload + * + * This header is prepended to each received event message payload before + * queueing it on the related &struct events_queue. + */ +struct scmi_event_header { + ktime_t timestamp; + size_t payld_sz; + unsigned char evt_id; + unsigned char payld[]; +}; + +struct scmi_registered_event; + +/** + * struct scmi_registered_events_desc - Protocol Specific information + * @id: Protocol ID + * @ops: Protocol specific and event-related operations + * @equeue: The embedded per-protocol events_queue + * @ni: A reference to the initialized instance descriptor + * @eh: A reference to pre-allocated buffer to be used as a scratch area by the + * deferred worker when fetching data from the kfifo + * @eh_sz: Size of the pre-allocated buffer @eh + * @in_flight: A reference to an in flight &struct scmi_registered_event + * @num_events: Number of events in @registered_events + * @registered_events: A dynamically allocated array holding all the registered + * events' descriptors, whose fixed-size is determined at + * compile time. + * @registered_mtx: A mutex to protect @registered_events_handlers + * @registered_events_handlers: An hashtable containing all events' handlers + * descriptors registered for this protocol + * + * All protocols that register at least one event have their protocol-specific + * information stored here, together with the embedded allocated events_queue. + * These descriptors are stored in the @registered_protocols array at protocol + * registration time. + * + * Once these descriptors are successfully registered, they are NEVER again + * removed or modified since protocols do not unregister ever, so that, once + * we safely grab a NON-NULL reference from the array we can keep it and use it. + */ +struct scmi_registered_events_desc { + u8 id; + const struct scmi_event_ops *ops; + struct events_queue equeue; + struct scmi_notify_instance *ni; + struct scmi_event_header *eh; + size_t eh_sz; + void *in_flight; + int num_events; + struct scmi_registered_event **registered_events; + /* mutex to protect registered_events_handlers */ + struct mutex registered_mtx; + DECLARE_HASHTABLE(registered_events_handlers, SCMI_REGISTERED_HASH_SZ); +}; + +/** + * struct scmi_registered_event - Event Specific Information + * @proto: A reference to the associated protocol descriptor + * @evt: A reference to the associated event descriptor (as provided at + * registration time) + * @report: A pre-allocated buffer used by the deferred worker to fill a + * customized event report + * @num_sources: The number of possible sources for this event as stated at + * events' registration time + * @sources: A reference to a dynamically allocated array used to refcount the + * events' enable requests for all the existing sources + * @sources_mtx: A mutex to serialize the access to @sources + * + * All registered events are represented by one of these structures that are + * stored in the @registered_events array at protocol registration time. + * + * Once these descriptors are successfully registered, they are NEVER again + * removed or modified since protocols do not unregister ever, so that once we + * safely grab a NON-NULL reference from the table we can keep it and use it. + */ +struct scmi_registered_event { + struct scmi_registered_events_desc *proto; + const struct scmi_event *evt; + void *report; + u32 num_sources; + refcount_t *sources; + /* locking to serialize the access to sources */ + struct mutex sources_mtx; +}; + +/** + * struct scmi_event_handler - Event handler information + * @key: The used hashkey + * @users: A reference count for number of active users for this handler + * @r_evt: A reference to the associated registered event; when this is NULL + * this handler is pending, which means that identifies a set of + * callbacks intended to be attached to an event which is still not + * known nor registered by any protocol at that point in time + * @chain: The notification chain dedicated to this specific event tuple + * @hash: The hlist_node used for collision handling + * @enabled: A boolean which records if event's generation has been already + * enabled for this handler as a whole + * + * This structure collects all the information needed to process a received + * event identified by the tuple (proto_id, evt_id, src_id). + * These descriptors are stored in a per-protocol @registered_events_handlers + * table using as a key a value derived from that tuple. + */ +struct scmi_event_handler { + u32 key; + refcount_t users; + struct scmi_registered_event *r_evt; + struct blocking_notifier_head chain; + struct hlist_node hash; + bool enabled; +}; + +#define IS_HNDL_PENDING(hndl) (!(hndl)->r_evt) + +static struct scmi_event_handler * +scmi_get_active_handler(struct scmi_notify_instance *ni, u32 evt_key); +static void scmi_put_active_handler(struct scmi_notify_instance *ni, + struct scmi_event_handler *hndl); +static void scmi_put_handler_unlocked(struct scmi_notify_instance *ni, + struct scmi_event_handler *hndl); + +/** + * scmi_lookup_and_call_event_chain() - Lookup the proper chain and call it + * @ni: A reference to the notification instance to use + * @evt_key: The key to use to lookup the related notification chain + * @report: The customized event-specific report to pass down to the callbacks + * as their *data parameter. + */ +static inline void +scmi_lookup_and_call_event_chain(struct scmi_notify_instance *ni, + u32 evt_key, void *report) +{ + int ret; + struct scmi_event_handler *hndl; + + /* + * Here ensure the event handler cannot vanish while using it. + * It is legitimate, though, for an handler not to be found at all here, + * e.g. when it has been unregistered by the user after some events had + * already been queued. + */ + hndl = scmi_get_active_handler(ni, evt_key); + if (!hndl) + return; + + ret = blocking_notifier_call_chain(&hndl->chain, + KEY_XTRACT_EVT_ID(evt_key), + report); + /* Notifiers are NOT supposed to cut the chain ... */ + WARN_ON_ONCE(ret & NOTIFY_STOP_MASK); + + scmi_put_active_handler(ni, hndl); +} + +/** + * scmi_process_event_header() - Dequeue and process an event header + * @eq: The queue to use + * @pd: The protocol descriptor to use + * + * Read an event header from the protocol queue into the dedicated scratch + * buffer and looks for a matching registered event; in case an anomalously + * sized read is detected just flush the queue. + * + * Return: + * * a reference to the matching registered event when found + * * ERR_PTR(-EINVAL) when NO registered event could be found + * * NULL when the queue is empty + */ +static inline struct scmi_registered_event * +scmi_process_event_header(struct events_queue *eq, + struct scmi_registered_events_desc *pd) +{ + unsigned int outs; + struct scmi_registered_event *r_evt; + + outs = kfifo_out(&eq->kfifo, pd->eh, + sizeof(struct scmi_event_header)); + if (!outs) + return NULL; + if (outs != sizeof(struct scmi_event_header)) { + dev_err(pd->ni->handle->dev, "corrupted EVT header. Flush.\n"); + kfifo_reset_out(&eq->kfifo); + return NULL; + } + + r_evt = SCMI_GET_REVT_FROM_PD(pd, pd->eh->evt_id); + if (!r_evt) + r_evt = ERR_PTR(-EINVAL); + + return r_evt; +} + +/** + * scmi_process_event_payload() - Dequeue and process an event payload + * @eq: The queue to use + * @pd: The protocol descriptor to use + * @r_evt: The registered event descriptor to use + * + * Read an event payload from the protocol queue into the dedicated scratch + * buffer, fills a custom report and then look for matching event handlers and + * call them; skip any unknown event (as marked by scmi_process_event_header()) + * and in case an anomalously sized read is detected just flush the queue. + * + * Return: False when the queue is empty + */ +static inline bool +scmi_process_event_payload(struct events_queue *eq, + struct scmi_registered_events_desc *pd, + struct scmi_registered_event *r_evt) +{ + u32 src_id, key; + unsigned int outs; + void *report = NULL; + + outs = kfifo_out(&eq->kfifo, pd->eh->payld, pd->eh->payld_sz); + if (!outs) + return false; + + /* Any in-flight event has now been officially processed */ + pd->in_flight = NULL; + + if (outs != pd->eh->payld_sz) { + dev_err(pd->ni->handle->dev, "corrupted EVT Payload. Flush.\n"); + kfifo_reset_out(&eq->kfifo); + return false; + } + + if (IS_ERR(r_evt)) { + dev_warn(pd->ni->handle->dev, + "SKIP UNKNOWN EVT - proto:%X evt:%d\n", + pd->id, pd->eh->evt_id); + return true; + } + + report = REVT_FILL_REPORT(r_evt, pd->eh->evt_id, pd->eh->timestamp, + pd->eh->payld, pd->eh->payld_sz, + r_evt->report, &src_id); + if (!report) { + dev_err(pd->ni->handle->dev, + "report not available - proto:%X evt:%d\n", + pd->id, pd->eh->evt_id); + return true; + } + + /* At first search for a generic ALL src_ids handler... */ + key = MAKE_ALL_SRCS_KEY(pd->id, pd->eh->evt_id); + scmi_lookup_and_call_event_chain(pd->ni, key, report); + + /* ...then search for any specific src_id */ + key = MAKE_HASH_KEY(pd->id, pd->eh->evt_id, src_id); + scmi_lookup_and_call_event_chain(pd->ni, key, report); + + return true; +} + +/** + * scmi_events_dispatcher() - Common worker logic for all work items. + * @work: The work item to use, which is associated to a dedicated events_queue + * + * Logic: + * 1. dequeue one pending RX notification (queued in SCMI RX ISR context) + * 2. generate a custom event report from the received event message + * 3. lookup for any registered ALL_SRC_IDs handler: + * - > call the related notification chain passing in the report + * 4. lookup for any registered specific SRC_ID handler: + * - > call the related notification chain passing in the report + * + * Note that: + * * a dedicated per-protocol kfifo queue is used: in this way an anomalous + * flood of events cannot saturate other protocols' queues. + * * each per-protocol queue is associated to a distinct work_item, which + * means, in turn, that: + * + all protocols can process their dedicated queues concurrently + * (since notify_wq:max_active != 1) + * + anyway at most one worker instance is allowed to run on the same queue + * concurrently: this ensures that we can have only one concurrent + * reader/writer on the associated kfifo, so that we can use it lock-less + * + * Context: Process context. + */ +static void scmi_events_dispatcher(struct work_struct *work) +{ + struct events_queue *eq; + struct scmi_registered_events_desc *pd; + struct scmi_registered_event *r_evt; + + eq = container_of(work, struct events_queue, notify_work); + pd = container_of(eq, struct scmi_registered_events_desc, equeue); + /* + * In order to keep the queue lock-less and the number of memcopies + * to the bare minimum needed, the dispatcher accounts for the + * possibility of per-protocol in-flight events: i.e. an event whose + * reception could end up being split across two subsequent runs of this + * worker, first the header, then the payload. + */ + do { + if (!pd->in_flight) { + r_evt = scmi_process_event_header(eq, pd); + if (!r_evt) + break; + pd->in_flight = r_evt; + } else { + r_evt = pd->in_flight; + } + } while (scmi_process_event_payload(eq, pd, r_evt)); +} + +/** + * scmi_notify() - Queues a notification for further deferred processing + * @handle: The handle identifying the platform instance from which the + * dispatched event is generated + * @proto_id: Protocol ID + * @evt_id: Event ID (msgID) + * @buf: Event Message Payload (without the header) + * @len: Event Message Payload size + * @ts: RX Timestamp in nanoseconds (boottime) + * + * Context: Called in interrupt context to queue a received event for + * deferred processing. + * + * Return: 0 on Success + */ +int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, + const void *buf, size_t len, ktime_t ts) +{ + struct scmi_registered_event *r_evt; + struct scmi_event_header eh; + struct scmi_notify_instance *ni; + + /* Ensure notify_priv is updated */ + smp_rmb(); + if (!handle->notify_priv) + return 0; + ni = handle->notify_priv; + + r_evt = SCMI_GET_REVT(ni, proto_id, evt_id); + if (!r_evt) + return -EINVAL; + + if (len > r_evt->evt->max_payld_sz) { + dev_err(handle->dev, "discard badly sized message\n"); + return -EINVAL; + } + if (kfifo_avail(&r_evt->proto->equeue.kfifo) < sizeof(eh) + len) { + dev_warn(handle->dev, + "queue full, dropping proto_id:%d evt_id:%d ts:%lld\n", + proto_id, evt_id, ktime_to_ns(ts)); + return -ENOMEM; + } + + eh.timestamp = ts; + eh.evt_id = evt_id; + eh.payld_sz = len; + /* + * Header and payload are enqueued with two distinct kfifo_in() (so non + * atomic), but this situation is handled properly on the consumer side + * with in-flight events tracking. + */ + kfifo_in(&r_evt->proto->equeue.kfifo, &eh, sizeof(eh)); + kfifo_in(&r_evt->proto->equeue.kfifo, buf, len); + /* + * Don't care about return value here since we just want to ensure that + * a work is queued all the times whenever some items have been pushed + * on the kfifo: + * - if work was already queued it will simply fail to queue a new one + * since it is not needed + * - if work was not queued already it will be now, even in case work + * was in fact already running: this behavior avoids any possible race + * when this function pushes new items onto the kfifos after the + * related executing worker had already determined the kfifo to be + * empty and it was terminating. + */ + queue_work(r_evt->proto->equeue.wq, + &r_evt->proto->equeue.notify_work); + + return 0; +} + +/** + * scmi_kfifo_free() - Devres action helper to free the kfifo + * @kfifo: The kfifo to free + */ +static void scmi_kfifo_free(void *kfifo) +{ + kfifo_free((struct kfifo *)kfifo); +} + +/** + * scmi_initialize_events_queue() - Allocate/Initialize a kfifo buffer + * @ni: A reference to the notification instance to use + * @equeue: The events_queue to initialize + * @sz: Size of the kfifo buffer to allocate + * + * Allocate a buffer for the kfifo and initialize it. + * + * Return: 0 on Success + */ +static int scmi_initialize_events_queue(struct scmi_notify_instance *ni, + struct events_queue *equeue, size_t sz) +{ + int ret; + + if (kfifo_alloc(&equeue->kfifo, sz, GFP_KERNEL)) + return -ENOMEM; + /* Size could have been roundup to power-of-two */ + equeue->sz = kfifo_size(&equeue->kfifo); + + ret = devm_add_action_or_reset(ni->handle->dev, scmi_kfifo_free, + &equeue->kfifo); + if (ret) + return ret; + + INIT_WORK(&equeue->notify_work, scmi_events_dispatcher); + equeue->wq = ni->notify_wq; + + return ret; +} + +/** + * scmi_allocate_registered_events_desc() - Allocate a registered events' + * descriptor + * @ni: A reference to the &struct scmi_notify_instance notification instance + * to use + * @proto_id: Protocol ID + * @queue_sz: Size of the associated queue to allocate + * @eh_sz: Size of the event header scratch area to pre-allocate + * @num_events: Number of events to support (size of @registered_events) + * @ops: Pointer to a struct holding references to protocol specific helpers + * needed during events handling + * + * It is supposed to be called only once for each protocol at protocol + * initialization time, so it warns if the requested protocol is found already + * registered. + * + * Return: The allocated and registered descriptor on Success + */ +static struct scmi_registered_events_desc * +scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni, + u8 proto_id, size_t queue_sz, size_t eh_sz, + int num_events, + const struct scmi_event_ops *ops) +{ + int ret; + struct scmi_registered_events_desc *pd; + + /* Ensure protocols are up to date */ + smp_rmb(); + if (WARN_ON(ni->registered_protocols[proto_id])) + return ERR_PTR(-EINVAL); + + pd = devm_kzalloc(ni->handle->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return ERR_PTR(-ENOMEM); + pd->id = proto_id; + pd->ops = ops; + pd->ni = ni; + + ret = scmi_initialize_events_queue(ni, &pd->equeue, queue_sz); + if (ret) + return ERR_PTR(ret); + + pd->eh = devm_kzalloc(ni->handle->dev, eh_sz, GFP_KERNEL); + if (!pd->eh) + return ERR_PTR(-ENOMEM); + pd->eh_sz = eh_sz; + + pd->registered_events = devm_kcalloc(ni->handle->dev, num_events, + sizeof(char *), GFP_KERNEL); + if (!pd->registered_events) + return ERR_PTR(-ENOMEM); + pd->num_events = num_events; + + /* Initialize per protocol handlers table */ + mutex_init(&pd->registered_mtx); + hash_init(pd->registered_events_handlers); + + return pd; +} + +/** + * scmi_register_protocol_events() - Register Protocol Events with the core + * @handle: The handle identifying the platform instance against which the + * the protocol's events are registered + * @proto_id: Protocol ID + * @queue_sz: Size in bytes of the associated queue to be allocated + * @ops: Protocol specific event-related operations + * @evt: Event descriptor array + * @num_events: Number of events in @evt array + * @num_sources: Number of possible sources for this protocol on this + * platform. + * + * Used by SCMI Protocols initialization code to register with the notification + * core the list of supported events and their descriptors: takes care to + * pre-allocate and store all needed descriptors, scratch buffers and event + * queues. + * + * Return: 0 on Success + */ +int scmi_register_protocol_events(const struct scmi_handle *handle, + u8 proto_id, size_t queue_sz, + const struct scmi_event_ops *ops, + const struct scmi_event *evt, int num_events, + int num_sources) +{ + int i; + size_t payld_sz = 0; + struct scmi_registered_events_desc *pd; + struct scmi_notify_instance *ni; + + if (!ops || !evt) + return -EINVAL; + + /* Ensure notify_priv is updated */ + smp_rmb(); + if (!handle->notify_priv) + return -ENOMEM; + ni = handle->notify_priv; + + /* Attach to the notification main devres group */ + if (!devres_open_group(ni->handle->dev, ni->gid, GFP_KERNEL)) + return -ENOMEM; + + for (i = 0; i < num_events; i++) + payld_sz = max_t(size_t, payld_sz, evt[i].max_payld_sz); + payld_sz += sizeof(struct scmi_event_header); + + pd = scmi_allocate_registered_events_desc(ni, proto_id, queue_sz, + payld_sz, num_events, ops); + if (IS_ERR(pd)) + goto err; + + for (i = 0; i < num_events; i++, evt++) { + struct scmi_registered_event *r_evt; + + r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt), + GFP_KERNEL); + if (!r_evt) + goto err; + r_evt->proto = pd; + r_evt->evt = evt; + + r_evt->sources = devm_kcalloc(ni->handle->dev, num_sources, + sizeof(refcount_t), GFP_KERNEL); + if (!r_evt->sources) + goto err; + r_evt->num_sources = num_sources; + mutex_init(&r_evt->sources_mtx); + + r_evt->report = devm_kzalloc(ni->handle->dev, + evt->max_report_sz, GFP_KERNEL); + if (!r_evt->report) + goto err; + + pd->registered_events[i] = r_evt; + /* Ensure events are updated */ + smp_wmb(); + dev_dbg(handle->dev, "registered event - %lX\n", + MAKE_ALL_SRCS_KEY(r_evt->proto->id, r_evt->evt->id)); + } + + /* Register protocol and events...it will never be removed */ + ni->registered_protocols[proto_id] = pd; + /* Ensure protocols are updated */ + smp_wmb(); + + devres_close_group(ni->handle->dev, ni->gid); + + /* + * Finalize any pending events' handler which could have been waiting + * for this protocol's events registration. + */ + schedule_work(&ni->init_work); + + return 0; + +err: + dev_warn(handle->dev, "Proto:%X - Registration Failed !\n", proto_id); + /* A failing protocol registration does not trigger full failure */ + devres_close_group(ni->handle->dev, ni->gid); + + return -ENOMEM; +} + +/** + * scmi_allocate_event_handler() - Allocate Event handler + * @ni: A reference to the notification instance to use + * @evt_key: 32bit key uniquely bind to the event identified by the tuple + * (proto_id, evt_id, src_id) + * + * Allocate an event handler and related notification chain associated with + * the provided event handler key. + * Note that, at this point, a related registered_event is still to be + * associated to this handler descriptor (hndl->r_evt == NULL), so the handler + * is initialized as pending. + * + * Context: Assumes to be called with @pending_mtx already acquired. + * Return: the freshly allocated structure on Success + */ +static struct scmi_event_handler * +scmi_allocate_event_handler(struct scmi_notify_instance *ni, u32 evt_key) +{ + struct scmi_event_handler *hndl; + + hndl = kzalloc(sizeof(*hndl), GFP_KERNEL); + if (!hndl) + return NULL; + hndl->key = evt_key; + BLOCKING_INIT_NOTIFIER_HEAD(&hndl->chain); + refcount_set(&hndl->users, 1); + /* New handlers are created pending */ + hash_add(ni->pending_events_handlers, &hndl->hash, hndl->key); + + return hndl; +} + +/** + * scmi_free_event_handler() - Free the provided Event handler + * @hndl: The event handler structure to free + * + * Context: Assumes to be called with proper locking acquired depending + * on the situation. + */ +static void scmi_free_event_handler(struct scmi_event_handler *hndl) +{ + hash_del(&hndl->hash); + kfree(hndl); +} + +/** + * scmi_bind_event_handler() - Helper to attempt binding an handler to an event + * @ni: A reference to the notification instance to use + * @hndl: The event handler to bind + * + * If an associated registered event is found, move the handler from the pending + * into the registered table. + * + * Context: Assumes to be called with @pending_mtx already acquired. + * + * Return: 0 on Success + */ +static inline int scmi_bind_event_handler(struct scmi_notify_instance *ni, + struct scmi_event_handler *hndl) +{ + struct scmi_registered_event *r_evt; + + r_evt = SCMI_GET_REVT(ni, KEY_XTRACT_PROTO_ID(hndl->key), + KEY_XTRACT_EVT_ID(hndl->key)); + if (!r_evt) + return -EINVAL; + + /* Remove from pending and insert into registered */ + hash_del(&hndl->hash); + hndl->r_evt = r_evt; + mutex_lock(&r_evt->proto->registered_mtx); + hash_add(r_evt->proto->registered_events_handlers, + &hndl->hash, hndl->key); + mutex_unlock(&r_evt->proto->registered_mtx); + + return 0; +} + +/** + * scmi_valid_pending_handler() - Helper to check pending status of handlers + * @ni: A reference to the notification instance to use + * @hndl: The event handler to check + * + * An handler is considered pending when its r_evt == NULL, because the related + * event was still unknown at handler's registration time; anyway, since all + * protocols register their supported events once for all at protocols' + * initialization time, a pending handler cannot be considered valid anymore if + * the underlying event (which it is waiting for), belongs to an already + * initialized and registered protocol. + * + * Return: 0 on Success + */ +static inline int scmi_valid_pending_handler(struct scmi_notify_instance *ni, + struct scmi_event_handler *hndl) +{ + struct scmi_registered_events_desc *pd; + + if (!IS_HNDL_PENDING(hndl)) + return -EINVAL; + + pd = SCMI_GET_PROTO(ni, KEY_XTRACT_PROTO_ID(hndl->key)); + if (pd) + return -EINVAL; + + return 0; +} + +/** + * scmi_register_event_handler() - Register whenever possible an Event handler + * @ni: A reference to the notification instance to use + * @hndl: The event handler to register + * + * At first try to bind an event handler to its associated event, then check if + * it was at least a valid pending handler: if it was not bound nor valid return + * false. + * + * Valid pending incomplete bindings will be periodically retried by a dedicated + * worker which is kicked each time a new protocol completes its own + * registration phase. + * + * Context: Assumes to be called with @pending_mtx acquired. + * + * Return: 0 on Success + */ +static int scmi_register_event_handler(struct scmi_notify_instance *ni, + struct scmi_event_handler *hndl) +{ + int ret; + + ret = scmi_bind_event_handler(ni, hndl); + if (!ret) { + dev_dbg(ni->handle->dev, "registered NEW handler - key:%X\n", + hndl->key); + } else { + ret = scmi_valid_pending_handler(ni, hndl); + if (!ret) + dev_dbg(ni->handle->dev, + "registered PENDING handler - key:%X\n", + hndl->key); + } + + return ret; +} + +/** + * __scmi_event_handler_get_ops() - Utility to get or create an event handler + * @ni: A reference to the notification instance to use + * @evt_key: The event key to use + * @create: A boolean flag to specify if a handler must be created when + * not already existent + * + * Search for the desired handler matching the key in both the per-protocol + * registered table and the common pending table: + * * if found adjust users refcount + * * if not found and @create is true, create and register the new handler: + * handler could end up being registered as pending if no matching event + * could be found. + * + * An handler is guaranteed to reside in one and only one of the tables at + * any one time; to ensure this the whole search and create is performed + * holding the @pending_mtx lock, with @registered_mtx additionally acquired + * if needed. + * + * Note that when a nested acquisition of these mutexes is needed the locking + * order is always (same as in @init_work): + * 1. pending_mtx + * 2. registered_mtx + * + * Events generation is NOT enabled right after creation within this routine + * since at creation time we usually want to have all setup and ready before + * events really start flowing. + * + * Return: A properly refcounted handler on Success, NULL on Failure + */ +static inline struct scmi_event_handler * +__scmi_event_handler_get_ops(struct scmi_notify_instance *ni, + u32 evt_key, bool create) +{ + struct scmi_registered_event *r_evt; + struct scmi_event_handler *hndl = NULL; + + r_evt = SCMI_GET_REVT(ni, KEY_XTRACT_PROTO_ID(evt_key), + KEY_XTRACT_EVT_ID(evt_key)); + + mutex_lock(&ni->pending_mtx); + /* Search registered events at first ... if possible at all */ + if (r_evt) { + mutex_lock(&r_evt->proto->registered_mtx); + hndl = KEY_FIND(r_evt->proto->registered_events_handlers, + hndl, evt_key); + if (hndl) + refcount_inc(&hndl->users); + mutex_unlock(&r_evt->proto->registered_mtx); + } + + /* ...then amongst pending. */ + if (!hndl) { + hndl = KEY_FIND(ni->pending_events_handlers, hndl, evt_key); + if (hndl) + refcount_inc(&hndl->users); + } + + /* Create if still not found and required */ + if (!hndl && create) { + hndl = scmi_allocate_event_handler(ni, evt_key); + if (hndl && scmi_register_event_handler(ni, hndl)) { + dev_dbg(ni->handle->dev, + "purging UNKNOWN handler - key:%X\n", + hndl->key); + /* this hndl can be only a pending one */ + scmi_put_handler_unlocked(ni, hndl); + hndl = NULL; + } + } + mutex_unlock(&ni->pending_mtx); + + return hndl; +} + +static struct scmi_event_handler * +scmi_get_handler(struct scmi_notify_instance *ni, u32 evt_key) +{ + return __scmi_event_handler_get_ops(ni, evt_key, false); +} + +static struct scmi_event_handler * +scmi_get_or_create_handler(struct scmi_notify_instance *ni, u32 evt_key) +{ + return __scmi_event_handler_get_ops(ni, evt_key, true); +} + +/** + * scmi_get_active_handler() - Helper to get active handlers only + * @ni: A reference to the notification instance to use + * @evt_key: The event key to use + * + * Search for the desired handler matching the key only in the per-protocol + * table of registered handlers: this is called only from the dispatching path + * so want to be as quick as possible and do not care about pending. + * + * Return: A properly refcounted active handler + */ +static struct scmi_event_handler * +scmi_get_active_handler(struct scmi_notify_instance *ni, u32 evt_key) +{ + struct scmi_registered_event *r_evt; + struct scmi_event_handler *hndl = NULL; + + r_evt = SCMI_GET_REVT(ni, KEY_XTRACT_PROTO_ID(evt_key), + KEY_XTRACT_EVT_ID(evt_key)); + if (r_evt) { + mutex_lock(&r_evt->proto->registered_mtx); + hndl = KEY_FIND(r_evt->proto->registered_events_handlers, + hndl, evt_key); + if (hndl) + refcount_inc(&hndl->users); + mutex_unlock(&r_evt->proto->registered_mtx); + } + + return hndl; +} + +/** + * __scmi_enable_evt() - Enable/disable events generation + * @r_evt: The registered event to act upon + * @src_id: The src_id to act upon + * @enable: The action to perform: true->Enable, false->Disable + * + * Takes care of proper refcounting while performing enable/disable: handles + * the special case of ALL sources requests by itself. + * Returns successfully if at least one of the required src_id has been + * successfully enabled/disabled. + * + * Return: 0 on Success + */ +static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt, + u32 src_id, bool enable) +{ + int retvals = 0; + u32 num_sources; + refcount_t *sid; + + if (src_id == SRC_ID_MASK) { + src_id = 0; + num_sources = r_evt->num_sources; + } else if (src_id < r_evt->num_sources) { + num_sources = 1; + } else { + return -EINVAL; + } + + mutex_lock(&r_evt->sources_mtx); + if (enable) { + for (; num_sources; src_id++, num_sources--) { + int ret = 0; + + sid = &r_evt->sources[src_id]; + if (refcount_read(sid) == 0) { + ret = REVT_NOTIFY_ENABLE(r_evt, r_evt->evt->id, + src_id); + if (!ret) + refcount_set(sid, 1); + } else { + refcount_inc(sid); + } + retvals += !ret; + } + } else { + for (; num_sources; src_id++, num_sources--) { + sid = &r_evt->sources[src_id]; + if (refcount_dec_and_test(sid)) + REVT_NOTIFY_DISABLE(r_evt, + r_evt->evt->id, src_id); + } + retvals = 1; + } + mutex_unlock(&r_evt->sources_mtx); + + return retvals ? 0 : -EINVAL; +} + +static int scmi_enable_events(struct scmi_event_handler *hndl) +{ + int ret = 0; + + if (!hndl->enabled) { + ret = __scmi_enable_evt(hndl->r_evt, + KEY_XTRACT_SRC_ID(hndl->key), true); + if (!ret) + hndl->enabled = true; + } + + return ret; +} + +static int scmi_disable_events(struct scmi_event_handler *hndl) +{ + int ret = 0; + + if (hndl->enabled) { + ret = __scmi_enable_evt(hndl->r_evt, + KEY_XTRACT_SRC_ID(hndl->key), false); + if (!ret) + hndl->enabled = false; + } + + return ret; +} + +/** + * scmi_put_handler_unlocked() - Put an event handler + * @ni: A reference to the notification instance to use + * @hndl: The event handler to act upon + * + * After having got exclusive access to the registered handlers hashtable, + * update the refcount and if @hndl is no more in use by anyone: + * * ask for events' generation disabling + * * unregister and free the handler itself + * + * Context: Assumes all the proper locking has been managed by the caller. + */ +static void scmi_put_handler_unlocked(struct scmi_notify_instance *ni, + struct scmi_event_handler *hndl) +{ + if (refcount_dec_and_test(&hndl->users)) { + if (!IS_HNDL_PENDING(hndl)) + scmi_disable_events(hndl); + scmi_free_event_handler(hndl); + } +} + +static void scmi_put_handler(struct scmi_notify_instance *ni, + struct scmi_event_handler *hndl) +{ + struct scmi_registered_event *r_evt = hndl->r_evt; + + mutex_lock(&ni->pending_mtx); + if (r_evt) + mutex_lock(&r_evt->proto->registered_mtx); + + scmi_put_handler_unlocked(ni, hndl); + + if (r_evt) + mutex_unlock(&r_evt->proto->registered_mtx); + mutex_unlock(&ni->pending_mtx); +} + +static void scmi_put_active_handler(struct scmi_notify_instance *ni, + struct scmi_event_handler *hndl) +{ + struct scmi_registered_event *r_evt = hndl->r_evt; + + mutex_lock(&r_evt->proto->registered_mtx); + scmi_put_handler_unlocked(ni, hndl); + mutex_unlock(&r_evt->proto->registered_mtx); +} + +/** + * scmi_event_handler_enable_events() - Enable events associated to an handler + * @hndl: The Event handler to act upon + * + * Return: 0 on Success + */ +static int scmi_event_handler_enable_events(struct scmi_event_handler *hndl) +{ + if (scmi_enable_events(hndl)) { + pr_err("Failed to ENABLE events for key:%X !\n", hndl->key); + return -EINVAL; + } + + return 0; +} + +/** + * scmi_register_notifier() - Register a notifier_block for an event + * @handle: The handle identifying the platform instance against which the + * callback is registered + * @proto_id: Protocol ID + * @evt_id: Event ID + * @src_id: Source ID, when NULL register for events coming form ALL possible + * sources + * @nb: A standard notifier block to register for the specified event + * + * Generic helper to register a notifier_block against a protocol event. + * + * A notifier_block @nb will be registered for each distinct event identified + * by the tuple (proto_id, evt_id, src_id) on a dedicated notification chain + * so that: + * + * (proto_X, evt_Y, src_Z) --> chain_X_Y_Z + * + * @src_id meaning is protocol specific and identifies the origin of the event + * (like domain_id, sensor_id and so forth). + * + * @src_id can be NULL to signify that the caller is interested in receiving + * notifications from ALL the available sources for that protocol OR simply that + * the protocol does not support distinct sources. + * + * As soon as one user for the specified tuple appears, an handler is created, + * and that specific event's generation is enabled at the platform level, unless + * an associated registered event is found missing, meaning that the needed + * protocol is still to be initialized and the handler has just been registered + * as still pending. + * + * Return: 0 on Success + */ +static int scmi_register_notifier(const struct scmi_handle *handle, + u8 proto_id, u8 evt_id, u32 *src_id, + struct notifier_block *nb) +{ + int ret = 0; + u32 evt_key; + struct scmi_event_handler *hndl; + struct scmi_notify_instance *ni; + + /* Ensure notify_priv is updated */ + smp_rmb(); + if (!handle->notify_priv) + return -ENODEV; + ni = handle->notify_priv; + + evt_key = MAKE_HASH_KEY(proto_id, evt_id, + src_id ? *src_id : SRC_ID_MASK); + hndl = scmi_get_or_create_handler(ni, evt_key); + if (!hndl) + return -EINVAL; + + blocking_notifier_chain_register(&hndl->chain, nb); + + /* Enable events for not pending handlers */ + if (!IS_HNDL_PENDING(hndl)) { + ret = scmi_event_handler_enable_events(hndl); + if (ret) + scmi_put_handler(ni, hndl); + } + + return ret; +} + +/** + * scmi_unregister_notifier() - Unregister a notifier_block for an event + * @handle: The handle identifying the platform instance against which the + * callback is unregistered + * @proto_id: Protocol ID + * @evt_id: Event ID + * @src_id: Source ID + * @nb: The notifier_block to unregister + * + * Takes care to unregister the provided @nb from the notification chain + * associated to the specified event and, if there are no more users for the + * event handler, frees also the associated event handler structures. + * (this could possibly cause disabling of event's generation at platform level) + * + * Return: 0 on Success + */ +static int scmi_unregister_notifier(const struct scmi_handle *handle, + u8 proto_id, u8 evt_id, u32 *src_id, + struct notifier_block *nb) +{ + u32 evt_key; + struct scmi_event_handler *hndl; + struct scmi_notify_instance *ni; + + /* Ensure notify_priv is updated */ + smp_rmb(); + if (!handle->notify_priv) + return -ENODEV; + ni = handle->notify_priv; + + evt_key = MAKE_HASH_KEY(proto_id, evt_id, + src_id ? *src_id : SRC_ID_MASK); + hndl = scmi_get_handler(ni, evt_key); + if (!hndl) + return -EINVAL; + + /* + * Note that this chain unregistration call is safe on its own + * being internally protected by an rwsem. + */ + blocking_notifier_chain_unregister(&hndl->chain, nb); + scmi_put_handler(ni, hndl); + + /* + * This balances the initial get issued in @scmi_register_notifier. + * If this notifier_block happened to be the last known user callback + * for this event, the handler is here freed and the event's generation + * stopped. + * + * Note that, an ongoing concurrent lookup on the delivery workqueue + * path could still hold the refcount to 1 even after this routine + * completes: in such a case it will be the final put on the delivery + * path which will finally free this unused handler. + */ + scmi_put_handler(ni, hndl); + + return 0; +} + +/** + * scmi_protocols_late_init() - Worker for late initialization + * @work: The work item to use associated to the proper SCMI instance + * + * This kicks in whenever a new protocol has completed its own registration via + * scmi_register_protocol_events(): it is in charge of scanning the table of + * pending handlers (registered by users while the related protocol was still + * not initialized) and finalizing their initialization whenever possible; + * invalid pending handlers are purged at this point in time. + */ +static void scmi_protocols_late_init(struct work_struct *work) +{ + int bkt; + struct scmi_event_handler *hndl; + struct scmi_notify_instance *ni; + struct hlist_node *tmp; + + ni = container_of(work, struct scmi_notify_instance, init_work); + + /* Ensure protocols and events are up to date */ + smp_rmb(); + + mutex_lock(&ni->pending_mtx); + hash_for_each_safe(ni->pending_events_handlers, bkt, tmp, hndl, hash) { + int ret; + + ret = scmi_bind_event_handler(ni, hndl); + if (!ret) { + dev_dbg(ni->handle->dev, + "finalized PENDING handler - key:%X\n", + hndl->key); + ret = scmi_event_handler_enable_events(hndl); + } else { + ret = scmi_valid_pending_handler(ni, hndl); + } + if (ret) { + dev_dbg(ni->handle->dev, + "purging PENDING handler - key:%X\n", + hndl->key); + /* this hndl can be only a pending one */ + scmi_put_handler_unlocked(ni, hndl); + } + } + mutex_unlock(&ni->pending_mtx); +} + +/* + * notify_ops are attached to the handle so that can be accessed + * directly from an scmi_driver to register its own notifiers. + */ +static struct scmi_notify_ops notify_ops = { + .register_event_notifier = scmi_register_notifier, + .unregister_event_notifier = scmi_unregister_notifier, +}; + +/** + * scmi_notification_init() - Initializes Notification Core Support + * @handle: The handle identifying the platform instance to initialize + * + * This function lays out all the basic resources needed by the notification + * core instance identified by the provided handle: once done, all of the + * SCMI Protocols can register their events with the core during their own + * initializations. + * + * Note that failing to initialize the core notifications support does not + * cause the whole SCMI Protocols stack to fail its initialization. + * + * SCMI Notification Initialization happens in 2 steps: + * * initialization: basic common allocations (this function) + * * registration: protocols asynchronously come into life and registers their + * own supported list of events with the core; this causes + * further per-protocol allocations + * + * Any user's callback registration attempt, referring a still not registered + * event, will be registered as pending and finalized later (if possible) + * by scmi_protocols_late_init() work. + * This allows for lazy initialization of SCMI Protocols due to late (or + * missing) SCMI drivers' modules loading. + * + * Return: 0 on Success + */ +int scmi_notification_init(struct scmi_handle *handle) +{ + void *gid; + struct scmi_notify_instance *ni; + + gid = devres_open_group(handle->dev, NULL, GFP_KERNEL); + if (!gid) + return -ENOMEM; + + ni = devm_kzalloc(handle->dev, sizeof(*ni), GFP_KERNEL); + if (!ni) + goto err; + + ni->gid = gid; + ni->handle = handle; + + ni->notify_wq = alloc_workqueue("scmi_notify", + WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS, + 0); + if (!ni->notify_wq) + goto err; + + ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTO, + sizeof(char *), GFP_KERNEL); + if (!ni->registered_protocols) + goto err; + + mutex_init(&ni->pending_mtx); + hash_init(ni->pending_events_handlers); + + INIT_WORK(&ni->init_work, scmi_protocols_late_init); + + handle->notify_ops = ¬ify_ops; + handle->notify_priv = ni; + /* Ensure handle is up to date */ + smp_wmb(); + + dev_info(handle->dev, "Core Enabled.\n"); + + devres_close_group(handle->dev, ni->gid); + + return 0; + +err: + dev_warn(handle->dev, "Initialization Failed.\n"); + devres_release_group(handle->dev, NULL); + return -ENOMEM; +} + +/** + * scmi_notification_exit() - Shutdown and clean Notification core + * @handle: The handle identifying the platform instance to shutdown + */ +void scmi_notification_exit(struct scmi_handle *handle) +{ + struct scmi_notify_instance *ni; + + /* Ensure notify_priv is updated */ + smp_rmb(); + if (!handle->notify_priv) + return; + ni = handle->notify_priv; + + handle->notify_priv = NULL; + /* Ensure handle is up to date */ + smp_wmb(); + + /* Destroy while letting pending work complete */ + destroy_workqueue(ni->notify_wq); + + devres_release_group(ni->handle->dev, ni->gid); +} diff --git a/drivers/firmware/arm_scmi/notify.h b/drivers/firmware/arm_scmi/notify.h new file mode 100644 index 000000000000..3485f20fa70e --- /dev/null +++ b/drivers/firmware/arm_scmi/notify.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * System Control and Management Interface (SCMI) Message Protocol + * notification header file containing some definitions, structures + * and function prototypes related to SCMI Notification handling. + * + * Copyright (C) 2020 ARM Ltd. + */ +#ifndef _SCMI_NOTIFY_H +#define _SCMI_NOTIFY_H + +#include <linux/device.h> +#include <linux/ktime.h> +#include <linux/types.h> + +#define SCMI_PROTO_QUEUE_SZ 4096 + +/** + * struct scmi_event - Describes an event to be supported + * @id: Event ID + * @max_payld_sz: Max possible size for the payload of a notification message + * @max_report_sz: Max possible size for the report of a notification message + * + * Each SCMI protocol, during its initialization phase, can describe the events + * it wishes to support in a few struct scmi_event and pass them to the core + * using scmi_register_protocol_events(). + */ +struct scmi_event { + u8 id; + size_t max_payld_sz; + size_t max_report_sz; +}; + +/** + * struct scmi_event_ops - Protocol helpers called by the notification core. + * @set_notify_enabled: Enable/disable the required evt_id/src_id notifications + * using the proper custom protocol commands. + * Return 0 on Success + * @fill_custom_report: fills a custom event report from the provided + * event message payld identifying the event + * specific src_id. + * Return NULL on failure otherwise @report now fully + * populated + * + * Context: Helpers described in &struct scmi_event_ops are called only in + * process context. + */ +struct scmi_event_ops { + int (*set_notify_enabled)(const struct scmi_handle *handle, + u8 evt_id, u32 src_id, bool enabled); + void *(*fill_custom_report)(const struct scmi_handle *handle, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id); +}; + +int scmi_notification_init(struct scmi_handle *handle); +void scmi_notification_exit(struct scmi_handle *handle); + +int scmi_register_protocol_events(const struct scmi_handle *handle, + u8 proto_id, size_t queue_sz, + const struct scmi_event_ops *ops, + const struct scmi_event *evt, int num_events, + int num_sources); +int scmi_notify(const struct scmi_handle *handle, u8 proto_id, u8 evt_id, + const void *buf, size_t len, ktime_t ts); + +#endif /* _SCMI_NOTIFY_H */ diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index eadc171e254b..3e1e87012c95 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -5,15 +5,19 @@ * Copyright (C) 2018 ARM Ltd. */ +#define pr_fmt(fmt) "SCMI Notifications PERF - " fmt + #include <linux/bits.h> #include <linux/of.h> #include <linux/io.h> #include <linux/io-64-nonatomic-hi-lo.h> #include <linux/platform_device.h> #include <linux/pm_opp.h> +#include <linux/scmi_protocol.h> #include <linux/sort.h> #include "common.h" +#include "notify.h" enum scmi_performance_protocol_cmd { PERF_DOMAIN_ATTRIBUTES = 0x3, @@ -27,11 +31,6 @@ enum scmi_performance_protocol_cmd { PERF_DESCRIBE_FASTCHANNEL = 0xb, }; -enum scmi_performance_protocol_notify { - PERFORMANCE_LIMITS_CHANGED = 0x0, - PERFORMANCE_LEVEL_CHANGED = 0x1, -}; - struct scmi_opp { u32 perf; u32 power; @@ -86,6 +85,19 @@ struct scmi_perf_notify_level_or_limits { __le32 notify_enable; }; +struct scmi_perf_limits_notify_payld { + __le32 agent_id; + __le32 domain_id; + __le32 range_max; + __le32 range_min; +}; + +struct scmi_perf_level_notify_payld { + __le32 agent_id; + __le32 domain_id; + __le32 performance_level; +}; + struct scmi_msg_resp_perf_describe_levels { __le16 num_returned; __le16 num_remaining; @@ -158,6 +170,11 @@ struct scmi_perf_info { struct perf_dom_info *dom_info; }; +static enum scmi_performance_protocol_cmd evt_2_cmd[] = { + PERF_NOTIFY_LIMITS, + PERF_NOTIFY_LEVEL, +}; + static int scmi_perf_attributes_get(const struct scmi_handle *handle, struct scmi_perf_info *pi) { @@ -488,6 +505,29 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, return scmi_perf_mb_level_get(handle, domain, level, poll); } +static int scmi_perf_level_limits_notify(const struct scmi_handle *handle, + u32 domain, int message_id, + bool enable) +{ + int ret; + struct scmi_xfer *t; + struct scmi_perf_notify_level_or_limits *notify; + + ret = scmi_xfer_get_init(handle, message_id, SCMI_PROTOCOL_PERF, + sizeof(*notify), 0, &t); + if (ret) + return ret; + + notify = t->tx.buf; + notify->domain = cpu_to_le32(domain); + notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size) { if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4) @@ -697,6 +737,17 @@ static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain, return ret; } +static bool scmi_fast_switch_possible(const struct scmi_handle *handle, + struct device *dev) +{ + struct perf_dom_info *dom; + struct scmi_perf_info *pi = handle->perf_priv; + + dom = pi->dom_info + scmi_dev_domain_id(dev); + + return dom->fc_info && dom->fc_info->level_set_addr; +} + static struct scmi_perf_ops perf_ops = { .limits_set = scmi_perf_limits_set, .limits_get = scmi_perf_limits_get, @@ -708,6 +759,90 @@ static struct scmi_perf_ops perf_ops = { .freq_set = scmi_dvfs_freq_set, .freq_get = scmi_dvfs_freq_get, .est_power_get = scmi_dvfs_est_power_get, + .fast_switch_possible = scmi_fast_switch_possible, +}; + +static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle, + u8 evt_id, u32 src_id, bool enable) +{ + int ret, cmd_id; + + if (evt_id >= ARRAY_SIZE(evt_2_cmd)) + return -EINVAL; + + cmd_id = evt_2_cmd[evt_id]; + ret = scmi_perf_level_limits_notify(handle, src_id, cmd_id, enable); + if (ret) + pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", + evt_id, src_id, ret); + + return ret; +} + +static void *scmi_perf_fill_custom_report(const struct scmi_handle *handle, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id) +{ + void *rep = NULL; + + switch (evt_id) { + case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED: + { + const struct scmi_perf_limits_notify_payld *p = payld; + struct scmi_perf_limits_report *r = report; + + if (sizeof(*p) != payld_sz) + break; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->domain_id = le32_to_cpu(p->domain_id); + r->range_max = le32_to_cpu(p->range_max); + r->range_min = le32_to_cpu(p->range_min); + *src_id = r->domain_id; + rep = r; + break; + } + case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED: + { + const struct scmi_perf_level_notify_payld *p = payld; + struct scmi_perf_level_report *r = report; + + if (sizeof(*p) != payld_sz) + break; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->domain_id = le32_to_cpu(p->domain_id); + r->performance_level = le32_to_cpu(p->performance_level); + *src_id = r->domain_id; + rep = r; + break; + } + default: + break; + } + + return rep; +} + +static const struct scmi_event perf_events[] = { + { + .id = SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, + .max_payld_sz = sizeof(struct scmi_perf_limits_notify_payld), + .max_report_sz = sizeof(struct scmi_perf_limits_report), + }, + { + .id = SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED, + .max_payld_sz = sizeof(struct scmi_perf_level_notify_payld), + .max_report_sz = sizeof(struct scmi_perf_level_report), + }, +}; + +static const struct scmi_event_ops perf_event_ops = { + .set_notify_enabled = scmi_perf_set_notify_enabled, + .fill_custom_report = scmi_perf_fill_custom_report, }; static int scmi_perf_protocol_init(struct scmi_handle *handle) @@ -742,6 +877,12 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle) scmi_perf_domain_init_fc(handle, domain, &dom->fc_info); } + scmi_register_protocol_events(handle, + SCMI_PROTOCOL_PERF, SCMI_PROTO_QUEUE_SZ, + &perf_event_ops, perf_events, + ARRAY_SIZE(perf_events), + pinfo->num_domains); + pinfo->version = version; handle->perf_ops = &perf_ops; handle->perf_priv = pinfo; diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index cf7f0312381b..46f213644c49 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -5,19 +5,18 @@ * Copyright (C) 2018 ARM Ltd. */ +#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt + +#include <linux/scmi_protocol.h> + #include "common.h" +#include "notify.h" enum scmi_power_protocol_cmd { POWER_DOMAIN_ATTRIBUTES = 0x3, POWER_STATE_SET = 0x4, POWER_STATE_GET = 0x5, POWER_STATE_NOTIFY = 0x6, - POWER_STATE_CHANGE_REQUESTED_NOTIFY = 0x7, -}; - -enum scmi_power_protocol_notify { - POWER_STATE_CHANGED = 0x0, - POWER_STATE_CHANGE_REQUESTED = 0x1, }; struct scmi_msg_resp_power_attributes { @@ -48,6 +47,12 @@ struct scmi_power_state_notify { __le32 notify_enable; }; +struct scmi_power_state_notify_payld { + __le32 agent_id; + __le32 domain_id; + __le32 power_state; +}; + struct power_dom_info { bool state_set_sync; bool state_set_async; @@ -186,6 +191,75 @@ static struct scmi_power_ops power_ops = { .state_get = scmi_power_state_get, }; +static int scmi_power_request_notify(const struct scmi_handle *handle, + u32 domain, bool enable) +{ + int ret; + struct scmi_xfer *t; + struct scmi_power_state_notify *notify; + + ret = scmi_xfer_get_init(handle, POWER_STATE_NOTIFY, + SCMI_PROTOCOL_POWER, sizeof(*notify), 0, &t); + if (ret) + return ret; + + notify = t->tx.buf; + notify->domain = cpu_to_le32(domain); + notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_power_set_notify_enabled(const struct scmi_handle *handle, + u8 evt_id, u32 src_id, bool enable) +{ + int ret; + + ret = scmi_power_request_notify(handle, src_id, enable); + if (ret) + pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n", + evt_id, src_id, ret); + + return ret; +} + +static void *scmi_power_fill_custom_report(const struct scmi_handle *handle, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id) +{ + const struct scmi_power_state_notify_payld *p = payld; + struct scmi_power_state_changed_report *r = report; + + if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz) + return NULL; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->domain_id = le32_to_cpu(p->domain_id); + r->power_state = le32_to_cpu(p->power_state); + *src_id = r->domain_id; + + return r; +} + +static const struct scmi_event power_events[] = { + { + .id = SCMI_EVENT_POWER_STATE_CHANGED, + .max_payld_sz = sizeof(struct scmi_power_state_notify_payld), + .max_report_sz = + sizeof(struct scmi_power_state_changed_report), + }, +}; + +static const struct scmi_event_ops power_event_ops = { + .set_notify_enabled = scmi_power_set_notify_enabled, + .fill_custom_report = scmi_power_fill_custom_report, +}; + static int scmi_power_protocol_init(struct scmi_handle *handle) { int domain; @@ -214,6 +288,12 @@ static int scmi_power_protocol_init(struct scmi_handle *handle) scmi_power_domain_attributes_get(handle, domain, dom); } + scmi_register_protocol_events(handle, + SCMI_PROTOCOL_POWER, SCMI_PROTO_QUEUE_SZ, + &power_event_ops, power_events, + ARRAY_SIZE(power_events), + pinfo->num_domains); + pinfo->version = version; handle->power_ops = &power_ops; handle->power_priv = pinfo; diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index de73054554f3..3691bafca057 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -5,7 +5,12 @@ * Copyright (C) 2019 ARM Ltd. */ +#define pr_fmt(fmt) "SCMI Notifications RESET - " fmt + +#include <linux/scmi_protocol.h> + #include "common.h" +#include "notify.h" enum scmi_reset_protocol_cmd { RESET_DOMAIN_ATTRIBUTES = 0x3, @@ -13,10 +18,6 @@ enum scmi_reset_protocol_cmd { RESET_NOTIFY = 0x5, }; -enum scmi_reset_protocol_notify { - RESET_ISSUED = 0x0, -}; - #define NUM_RESET_DOMAIN_MASK 0xffff #define RESET_NOTIFY_ENABLE BIT(0) @@ -40,6 +41,18 @@ struct scmi_msg_reset_domain_reset { #define ARCH_COLD_RESET (ARCH_RESET_TYPE | COLD_RESET_STATE) }; +struct scmi_msg_reset_notify { + __le32 id; + __le32 event_control; +#define RESET_TP_NOTIFY_ALL BIT(0) +}; + +struct scmi_reset_issued_notify_payld { + __le32 agent_id; + __le32 domain_id; + __le32 reset_state; +}; + struct reset_dom_info { bool async_reset; bool reset_notify; @@ -190,6 +203,75 @@ static struct scmi_reset_ops reset_ops = { .deassert = scmi_reset_domain_deassert, }; +static int scmi_reset_notify(const struct scmi_handle *handle, u32 domain_id, + bool enable) +{ + int ret; + u32 evt_cntl = enable ? RESET_TP_NOTIFY_ALL : 0; + struct scmi_xfer *t; + struct scmi_msg_reset_notify *cfg; + + ret = scmi_xfer_get_init(handle, RESET_NOTIFY, + SCMI_PROTOCOL_RESET, sizeof(*cfg), 0, &t); + if (ret) + return ret; + + cfg = t->tx.buf; + cfg->id = cpu_to_le32(domain_id); + cfg->event_control = cpu_to_le32(evt_cntl); + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_reset_set_notify_enabled(const struct scmi_handle *handle, + u8 evt_id, u32 src_id, bool enable) +{ + int ret; + + ret = scmi_reset_notify(handle, src_id, enable); + if (ret) + pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", + evt_id, src_id, ret); + + return ret; +} + +static void *scmi_reset_fill_custom_report(const struct scmi_handle *handle, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id) +{ + const struct scmi_reset_issued_notify_payld *p = payld; + struct scmi_reset_issued_report *r = report; + + if (evt_id != SCMI_EVENT_RESET_ISSUED || sizeof(*p) != payld_sz) + return NULL; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->domain_id = le32_to_cpu(p->domain_id); + r->reset_state = le32_to_cpu(p->reset_state); + *src_id = r->domain_id; + + return r; +} + +static const struct scmi_event reset_events[] = { + { + .id = SCMI_EVENT_RESET_ISSUED, + .max_payld_sz = sizeof(struct scmi_reset_issued_notify_payld), + .max_report_sz = sizeof(struct scmi_reset_issued_report), + }, +}; + +static const struct scmi_event_ops reset_event_ops = { + .set_notify_enabled = scmi_reset_set_notify_enabled, + .fill_custom_report = scmi_reset_fill_custom_report, +}; + static int scmi_reset_protocol_init(struct scmi_handle *handle) { int domain; @@ -218,6 +300,12 @@ static int scmi_reset_protocol_init(struct scmi_handle *handle) scmi_reset_domain_attributes_get(handle, domain, dom); } + scmi_register_protocol_events(handle, + SCMI_PROTOCOL_RESET, SCMI_PROTO_QUEUE_SZ, + &reset_event_ops, reset_events, + ARRAY_SIZE(reset_events), + pinfo->num_domains); + pinfo->version = version; handle->reset_ops = &reset_ops; handle->reset_priv = pinfo; diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c index bafbfe358f97..9e44479f0284 100644 --- a/drivers/firmware/arm_scmi/scmi_pm_domain.c +++ b/drivers/firmware/arm_scmi/scmi_pm_domain.c @@ -85,7 +85,10 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev) for (i = 0; i < num_domains; i++, scmi_pd++) { u32 state; - domains[i] = &scmi_pd->genpd; + if (handle->power_ops->state_get(handle, i, &state)) { + dev_warn(dev, "failed to get state for domain %d\n", i); + continue; + } scmi_pd->domain = i; scmi_pd->handle = handle; @@ -94,13 +97,10 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev) scmi_pd->genpd.power_off = scmi_pd_power_off; scmi_pd->genpd.power_on = scmi_pd_power_on; - if (handle->power_ops->state_get(handle, i, &state)) { - dev_warn(dev, "failed to get state for domain %d\n", i); - continue; - } - pm_genpd_init(&scmi_pd->genpd, NULL, state == SCMI_POWER_STATE_GENERIC_OFF); + + domains[i] = &scmi_pd->genpd; } scmi_pd_data->domains = domains; diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index db1b1ab303da..1af0ad362e82 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -5,7 +5,12 @@ * Copyright (C) 2018 ARM Ltd. */ +#define pr_fmt(fmt) "SCMI Notifications SENSOR - " fmt + +#include <linux/scmi_protocol.h> + #include "common.h" +#include "notify.h" enum scmi_sensor_protocol_cmd { SENSOR_DESCRIPTION_GET = 0x3, @@ -14,10 +19,6 @@ enum scmi_sensor_protocol_cmd { SENSOR_READING_GET = 0x6, }; -enum scmi_sensor_protocol_notify { - SENSOR_TRIP_POINT_EVENT = 0x0, -}; - struct scmi_msg_resp_sensor_attributes { __le16 num_sensors; u8 max_requests; @@ -71,6 +72,12 @@ struct scmi_msg_sensor_reading_get { #define SENSOR_READ_ASYNC BIT(0) }; +struct scmi_sensor_trip_notify_payld { + __le32 agent_id; + __le32 sensor_id; + __le32 trip_point_desc; +}; + struct sensors_info { u32 version; int num_sensors; @@ -271,11 +278,57 @@ static int scmi_sensor_count_get(const struct scmi_handle *handle) static struct scmi_sensor_ops sensor_ops = { .count_get = scmi_sensor_count_get, .info_get = scmi_sensor_info_get, - .trip_point_notify = scmi_sensor_trip_point_notify, .trip_point_config = scmi_sensor_trip_point_config, .reading_get = scmi_sensor_reading_get, }; +static int scmi_sensor_set_notify_enabled(const struct scmi_handle *handle, + u8 evt_id, u32 src_id, bool enable) +{ + int ret; + + ret = scmi_sensor_trip_point_notify(handle, src_id, enable); + if (ret) + pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", + evt_id, src_id, ret); + + return ret; +} + +static void *scmi_sensor_fill_custom_report(const struct scmi_handle *handle, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id) +{ + const struct scmi_sensor_trip_notify_payld *p = payld; + struct scmi_sensor_trip_point_report *r = report; + + if (evt_id != SCMI_EVENT_SENSOR_TRIP_POINT_EVENT || + sizeof(*p) != payld_sz) + return NULL; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->sensor_id = le32_to_cpu(p->sensor_id); + r->trip_point_desc = le32_to_cpu(p->trip_point_desc); + *src_id = r->sensor_id; + + return r; +} + +static const struct scmi_event sensor_events[] = { + { + .id = SCMI_EVENT_SENSOR_TRIP_POINT_EVENT, + .max_payld_sz = sizeof(struct scmi_sensor_trip_notify_payld), + .max_report_sz = sizeof(struct scmi_sensor_trip_point_report), + }, +}; + +static const struct scmi_event_ops sensor_event_ops = { + .set_notify_enabled = scmi_sensor_set_notify_enabled, + .fill_custom_report = scmi_sensor_fill_custom_report, +}; + static int scmi_sensors_protocol_init(struct scmi_handle *handle) { u32 version; @@ -299,6 +352,12 @@ static int scmi_sensors_protocol_init(struct scmi_handle *handle) scmi_sensor_description_get(handle, sinfo); + scmi_register_protocol_events(handle, + SCMI_PROTOCOL_SENSOR, SCMI_PROTO_QUEUE_SZ, + &sensor_event_ops, sensor_events, + ARRAY_SIZE(sensor_events), + sinfo->num_sensors); + sinfo->version = version; handle->sensor_ops = &sensor_ops; handle->sensor_priv = sinfo; diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 49bc4b0e8428..a1537d123e38 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -21,6 +21,7 @@ * * @cinfo: SCMI channel info * @shmem: Transmit/Receive shared memory area + * @shmem_lock: Lock to protect access to Tx/Rx shared memory area * @func_id: smc/hvc call function id */ diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index b6180023eba7..8b8127fa8955 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -262,7 +262,7 @@ struct dmi_system_event_log { u8 header_format; u8 type_descriptors_supported_count; u8 per_log_type_descriptor_length; - u8 supported_log_type_descriptos[0]; + u8 supported_log_type_descriptos[]; } __packed; #define DMI_SYSFS_SEL_FIELD(_field) \ diff --git a/drivers/firmware/google/memconsole-coreboot.c b/drivers/firmware/google/memconsole-coreboot.c index fd7f0fbec07e..d17e4d6ac9bc 100644 --- a/drivers/firmware/google/memconsole-coreboot.c +++ b/drivers/firmware/google/memconsole-coreboot.c @@ -21,7 +21,7 @@ struct cbmem_cons { u32 size_dont_access_after_boot; u32 cursor; - u8 body[0]; + u8 body[]; } __packed; #define CURSOR_MASK ((1 << 28) - 1) diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index db0812263d46..d23c5c69ab52 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -32,7 +32,7 @@ struct vpd_cbmem { u32 version; u32 ro_size; u32 rw_size; - u8 blob[0]; + u8 blob[]; }; struct vpd_section { diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 08bc9ddfbdfb..b76acbade2a0 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_IMX_DSP) += imx-dsp.o -obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c index db655e87cdc8..d9dcc20945c6 100644 --- a/drivers/firmware/imx/imx-scu-irq.c +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -10,6 +10,7 @@ #include <linux/firmware/imx/ipc.h> #include <linux/firmware/imx/sci.h> #include <linux/mailbox_client.h> +#include <linux/suspend.h> #define IMX_SC_IRQ_FUNC_ENABLE 1 #define IMX_SC_IRQ_FUNC_STATUS 2 @@ -91,6 +92,7 @@ static void imx_scu_irq_work_handler(struct work_struct *work) if (!irq_status) continue; + pm_system_wakeup(); imx_scu_irq_notifier_call_chain(irq_status, &i); } } diff --git a/drivers/soc/imx/soc-imx-scu.c b/drivers/firmware/imx/imx-scu-soc.c index 20d37eaeb5f2..2f32353de2c9 100644 --- a/drivers/soc/imx/soc-imx-scu.c +++ b/drivers/firmware/imx/imx-scu-soc.c @@ -10,9 +10,7 @@ #include <linux/platform_device.h> #include <linux/of.h> -#define IMX_SCU_SOC_DRIVER_NAME "imx-scu-soc" - -static struct imx_sc_ipc *soc_ipc_handle; +static struct imx_sc_ipc *imx_sc_soc_ipc_handle; struct imx_sc_msg_misc_get_soc_id { struct imx_sc_rpc_msg hdr; @@ -44,7 +42,7 @@ static int imx_scu_soc_uid(u64 *soc_uid) hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID; hdr->size = 1; - ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true); + ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true); if (ret) { pr_err("%s: get soc uid failed, ret %d\n", __func__, ret); return ret; @@ -71,7 +69,7 @@ static int imx_scu_soc_id(void) msg.data.req.control = IMX_SC_C_ID; msg.data.req.resource = IMX_SC_R_SYSTEM; - ret = imx_scu_call_rpc(soc_ipc_handle, &msg, true); + ret = imx_scu_call_rpc(imx_sc_soc_ipc_handle, &msg, true); if (ret) { pr_err("%s: get soc info failed, ret %d\n", __func__, ret); return ret; @@ -80,7 +78,7 @@ static int imx_scu_soc_id(void) return msg.data.resp.id; } -static int imx_scu_soc_probe(struct platform_device *pdev) +int imx_scu_soc_init(struct device *dev) { struct soc_device_attribute *soc_dev_attr; struct soc_device *soc_dev; @@ -88,11 +86,11 @@ static int imx_scu_soc_probe(struct platform_device *pdev) u64 uid = 0; u32 val; - ret = imx_scu_get_handle(&soc_ipc_handle); + ret = imx_scu_get_handle(&imx_sc_soc_ipc_handle); if (ret) return ret; - soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr), + soc_dev_attr = devm_kzalloc(dev, sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return -ENOMEM; @@ -115,73 +113,26 @@ static int imx_scu_soc_probe(struct platform_device *pdev) /* format soc_id value passed from SCU firmware */ val = id & 0x1f; - soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val); + soc_dev_attr->soc_id = devm_kasprintf(dev, GFP_KERNEL, "0x%x", val); if (!soc_dev_attr->soc_id) return -ENOMEM; /* format revision value passed from SCU firmware */ val = (id >> 5) & 0xf; val = (((val >> 2) + 1) << 4) | (val & 0x3); - soc_dev_attr->revision = kasprintf(GFP_KERNEL, - "%d.%d", - (val >> 4) & 0xf, - val & 0xf); - if (!soc_dev_attr->revision) { - ret = -ENOMEM; - goto free_soc_id; - } + soc_dev_attr->revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d", + (val >> 4) & 0xf, val & 0xf); + if (!soc_dev_attr->revision) + return -ENOMEM; - soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", uid); - if (!soc_dev_attr->serial_number) { - ret = -ENOMEM; - goto free_revision; - } + soc_dev_attr->serial_number = devm_kasprintf(dev, GFP_KERNEL, + "%016llX", uid); + if (!soc_dev_attr->serial_number) + return -ENOMEM; soc_dev = soc_device_register(soc_dev_attr); - if (IS_ERR(soc_dev)) { - ret = PTR_ERR(soc_dev); - goto free_serial_number; - } + if (IS_ERR(soc_dev)) + return PTR_ERR(soc_dev); return 0; - -free_serial_number: - kfree(soc_dev_attr->serial_number); -free_revision: - kfree(soc_dev_attr->revision); -free_soc_id: - kfree(soc_dev_attr->soc_id); - return ret; -} - -static struct platform_driver imx_scu_soc_driver = { - .driver = { - .name = IMX_SCU_SOC_DRIVER_NAME, - }, - .probe = imx_scu_soc_probe, -}; - -static int __init imx_scu_soc_init(void) -{ - struct platform_device *pdev; - struct device_node *np; - int ret; - - np = of_find_compatible_node(NULL, NULL, "fsl,imx-scu"); - if (!np) - return -ENODEV; - - of_node_put(np); - - ret = platform_driver_register(&imx_scu_soc_driver); - if (ret) - return ret; - - pdev = platform_device_register_simple(IMX_SCU_SOC_DRIVER_NAME, - -1, NULL, 0); - if (IS_ERR(pdev)) - platform_driver_unregister(&imx_scu_soc_driver); - - return PTR_ERR_OR_ZERO(pdev); } -device_initcall(imx_scu_soc_init); diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 2ab048222fe9..dca79caccd01 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -328,6 +328,10 @@ static int imx_scu_probe(struct platform_device *pdev) imx_sc_ipc_handle = sc_ipc; + ret = imx_scu_soc_init(dev); + if (ret) + dev_warn(dev, "failed to initialize SoC info: %d\n", ret); + ret = imx_scu_enable_general_irq_channel(dev); if (ret) dev_warn(dev, diff --git a/drivers/firmware/imx/rm.c b/drivers/firmware/imx/rm.c new file mode 100644 index 000000000000..a12db6ff323b --- /dev/null +++ b/drivers/firmware/imx/rm.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 NXP + * + * File containing client-side RPC functions for the RM service. These + * function are ported to clients that communicate to the SC. + */ + +#include <linux/firmware/imx/svc/rm.h> + +struct imx_sc_msg_rm_rsrc_owned { + struct imx_sc_rpc_msg hdr; + u16 resource; +} __packed __aligned(4); + +/* + * This function check @resource is owned by current partition or not + * + * @param[in] ipc IPC handle + * @param[in] resource resource the control is associated with + * + * @return Returns 0 for not owned and 1 for owned. + */ +bool imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource) +{ + struct imx_sc_msg_rm_rsrc_owned msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_RM; + hdr->func = IMX_SC_RM_FUNC_IS_RESOURCE_OWNED; + hdr->size = 2; + + msg.resource = resource; + + /* + * SCU firmware only returns value 0 or 1 + * for resource owned check which means not owned or owned. + * So it is always successful. + */ + imx_scu_call_rpc(ipc, &msg, true); + + return hdr->func; +} +EXPORT_SYMBOL(imx_sc_rm_is_resource_owned); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index fb5523aa16ee..af3d6d9ead28 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -167,8 +167,18 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, /* CM40 SS */ - { "cm40_i2c", IMX_SC_R_M4_0_I2C, 1, 0 }, - { "cm40_intmux", IMX_SC_R_M4_0_INTMUX, 1, 0 }, + { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 }, + { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 }, + { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0}, + { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0}, + { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0}, + + /* CM41 SS */ + { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, + { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, + { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0}, + { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0}, + { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0}, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c index 96758b71a8db..7127a04bca19 100644 --- a/drivers/firmware/iscsi_ibft.c +++ b/drivers/firmware/iscsi_ibft.c @@ -104,7 +104,7 @@ struct ibft_control { u16 tgt0_off; u16 nic1_off; u16 tgt1_off; - u16 expansion[0]; + u16 expansion[]; } __attribute__((__packed__)); struct ibft_initiator { diff --git a/drivers/firmware/pcdp.h b/drivers/firmware/pcdp.h index ce75d1da9e84..e02540571c52 100644 --- a/drivers/firmware/pcdp.h +++ b/drivers/firmware/pcdp.h @@ -103,6 +103,6 @@ struct pcdp { u8 creator_id[4]; u32 creator_rev; u32 num_uarts; - struct pcdp_uart uart[0]; /* actual size is num_uarts */ + struct pcdp_uart uart[]; /* actual size is num_uarts */ /* remainder of table is pcdp_device structures */ } __attribute__((packed)); diff --git a/drivers/firmware/smccc/Kconfig b/drivers/firmware/smccc/Kconfig index 27b675d76235..15e7466179a6 100644 --- a/drivers/firmware/smccc/Kconfig +++ b/drivers/firmware/smccc/Kconfig @@ -14,3 +14,12 @@ config HAVE_ARM_SMCCC_DISCOVERY to add SMCCC discovery mechanism though the PSCI firmware implementation of PSCI_FEATURES(SMCCC_VERSION) which returns success on firmware compliant to SMCCC v1.1 and above. + +config ARM_SMCCC_SOC_ID + bool "SoC bus device for the ARM SMCCC SOC_ID" + depends on HAVE_ARM_SMCCC_DISCOVERY + default y + select SOC_BUS + help + Include support for the SoC bus on the ARM SMCCC firmware based + platforms providing some sysfs information about the SoC variant. diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makefile index 6f369fe3f0b9..72ab84042832 100644 --- a/drivers/firmware/smccc/Makefile +++ b/drivers/firmware/smccc/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 # obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o +obj-$(CONFIG_ARM_SMCCC_SOC_ID) += soc_id.o diff --git a/drivers/firmware/smccc/soc_id.c b/drivers/firmware/smccc/soc_id.c new file mode 100644 index 000000000000..581aa5e9b077 --- /dev/null +++ b/drivers/firmware/smccc/soc_id.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020 Arm Limited + */ + +#define pr_fmt(fmt) "SMCCC: SOC_ID: " fmt + +#include <linux/arm-smccc.h> +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> + +#define SMCCC_SOC_ID_JEP106_BANK_IDX_MASK GENMASK(30, 24) +/* + * As per the SMC Calling Convention specification v1.2 (ARM DEN 0028C) + * Section 7.4 SMCCC_ARCH_SOC_ID bits[23:16] are JEP-106 identification + * code with parity bit for the SiP. We can drop the parity bit. + */ +#define SMCCC_SOC_ID_JEP106_ID_CODE_MASK GENMASK(22, 16) +#define SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK GENMASK(15, 0) + +#define JEP106_BANK_CONT_CODE(x) \ + (u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_BANK_IDX_MASK, (x))) +#define JEP106_ID_CODE(x) \ + (u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_ID_CODE_MASK, (x))) +#define IMP_DEF_SOC_ID(x) \ + (u16)(FIELD_GET(SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK, (x))) + +static struct soc_device *soc_dev; +static struct soc_device_attribute *soc_dev_attr; + +static int __init smccc_soc_init(void) +{ + struct arm_smccc_res res; + int soc_id_rev, soc_id_version; + static char soc_id_str[20], soc_id_rev_str[12]; + static char soc_id_jep106_id_str[12]; + + if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2) + return 0; + + if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE) { + pr_err("%s: invalid SMCCC conduit\n", __func__); + return -EOPNOTSUPP; + } + + arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, + ARM_SMCCC_ARCH_SOC_ID, &res); + + if (res.a0 == SMCCC_RET_NOT_SUPPORTED) { + pr_info("ARCH_SOC_ID not implemented, skipping ....\n"); + return 0; + } + + if ((int)res.a0 < 0) { + pr_info("ARCH_FEATURES(ARCH_SOC_ID) returned error: %lx\n", + res.a0); + return -EINVAL; + } + + arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res); + if ((int)res.a0 < 0) { + pr_err("ARCH_SOC_ID(0) returned error: %lx\n", res.a0); + return -EINVAL; + } + + soc_id_version = res.a0; + + arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res); + if ((int)res.a0 < 0) { + pr_err("ARCH_SOC_ID(1) returned error: %lx\n", res.a0); + return -EINVAL; + } + + soc_id_rev = res.a0; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + sprintf(soc_id_rev_str, "0x%08x", soc_id_rev); + sprintf(soc_id_jep106_id_str, "jep106:%02x%02x", + JEP106_BANK_CONT_CODE(soc_id_version), + JEP106_ID_CODE(soc_id_version)); + sprintf(soc_id_str, "%s:%04x", soc_id_jep106_id_str, + IMP_DEF_SOC_ID(soc_id_version)); + + soc_dev_attr->soc_id = soc_id_str; + soc_dev_attr->revision = soc_id_rev_str; + soc_dev_attr->family = soc_id_jep106_id_str; + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr); + return PTR_ERR(soc_dev); + } + + pr_info("ID = %s Revision = %s\n", soc_dev_attr->soc_id, + soc_dev_attr->revision); + + return 0; +} +module_init(smccc_soc_init); + +static void __exit smccc_soc_exit(void) +{ + if (soc_dev) + soc_device_unregister(soc_dev); + kfree(soc_dev_attr); +} +module_exit(smccc_soc_exit); diff --git a/drivers/firmware/tegra/bpmp-debugfs.c b/drivers/firmware/tegra/bpmp-debugfs.c index 636b40d4364d..c1bbba9ee93a 100644 --- a/drivers/firmware/tegra/bpmp-debugfs.c +++ b/drivers/firmware/tegra/bpmp-debugfs.c @@ -4,11 +4,14 @@ */ #include <linux/debugfs.h> #include <linux/dma-mapping.h> +#include <linux/slab.h> #include <linux/uaccess.h> #include <soc/tegra/bpmp.h> #include <soc/tegra/bpmp-abi.h> +static DEFINE_MUTEX(bpmp_debug_lock); + struct seqbuf { char *buf; size_t pos; @@ -96,6 +99,354 @@ static const char *get_filename(struct tegra_bpmp *bpmp, return filename; } +static int mrq_debug_open(struct tegra_bpmp *bpmp, const char *name, + uint32_t *fd, uint32_t *len, bool write) +{ + struct mrq_debug_request req = { + .cmd = cpu_to_le32(write ? CMD_DEBUG_OPEN_WO : CMD_DEBUG_OPEN_RO), + }; + struct mrq_debug_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_DEBUG, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + ssize_t sz_name; + int err = 0; + + sz_name = strscpy(req.fop.name, name, sizeof(req.fop.name)); + if (sz_name < 0) { + pr_err("File name too large: %s\n", name); + return -EINVAL; + } + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + else if (msg.rx.ret < 0) + return -EINVAL; + + *len = resp.fop.datalen; + *fd = resp.fop.fd; + + return 0; +} + +static int mrq_debug_close(struct tegra_bpmp *bpmp, uint32_t fd) +{ + struct mrq_debug_request req = { + .cmd = cpu_to_le32(CMD_DEBUG_CLOSE), + .frd = { + .fd = fd, + }, + }; + struct mrq_debug_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_DEBUG, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + int err = 0; + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + else if (msg.rx.ret < 0) + return -EINVAL; + + return 0; +} + +static int mrq_debug_read(struct tegra_bpmp *bpmp, const char *name, + char *data, size_t sz_data, uint32_t *nbytes) +{ + struct mrq_debug_request req = { + .cmd = cpu_to_le32(CMD_DEBUG_READ), + }; + struct mrq_debug_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_DEBUG, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + uint32_t fd = 0, len = 0; + int remaining, err; + + mutex_lock(&bpmp_debug_lock); + err = mrq_debug_open(bpmp, name, &fd, &len, 0); + if (err) + goto out; + + if (len > sz_data) { + err = -EFBIG; + goto close; + } + + req.frd.fd = fd; + remaining = len; + + while (remaining > 0) { + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) { + goto close; + } else if (msg.rx.ret < 0) { + err = -EINVAL; + goto close; + } + + if (resp.frd.readlen > remaining) { + pr_err("%s: read data length invalid\n", __func__); + err = -EINVAL; + goto close; + } + + memcpy(data, resp.frd.data, resp.frd.readlen); + data += resp.frd.readlen; + remaining -= resp.frd.readlen; + } + + *nbytes = len; + +close: + err = mrq_debug_close(bpmp, fd); +out: + mutex_unlock(&bpmp_debug_lock); + return err; +} + +static int mrq_debug_write(struct tegra_bpmp *bpmp, const char *name, + uint8_t *data, size_t sz_data) +{ + struct mrq_debug_request req = { + .cmd = cpu_to_le32(CMD_DEBUG_WRITE) + }; + struct mrq_debug_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_DEBUG, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + uint32_t fd = 0, len = 0; + size_t remaining; + int err; + + mutex_lock(&bpmp_debug_lock); + err = mrq_debug_open(bpmp, name, &fd, &len, 1); + if (err) + goto out; + + if (sz_data > len) { + err = -EINVAL; + goto close; + } + + req.fwr.fd = fd; + remaining = sz_data; + + while (remaining > 0) { + len = min(remaining, sizeof(req.fwr.data)); + memcpy(req.fwr.data, data, len); + req.fwr.datalen = len; + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) { + goto close; + } else if (msg.rx.ret < 0) { + err = -EINVAL; + goto close; + } + + data += req.fwr.datalen; + remaining -= req.fwr.datalen; + } + +close: + err = mrq_debug_close(bpmp, fd); +out: + mutex_unlock(&bpmp_debug_lock); + return err; +} + +static int bpmp_debug_show(struct seq_file *m, void *p) +{ + struct file *file = m->private; + struct inode *inode = file_inode(file); + struct tegra_bpmp *bpmp = inode->i_private; + char *databuf = NULL; + char fnamebuf[256]; + const char *filename; + uint32_t nbytes = 0; + size_t len; + int err; + + len = seq_get_buf(m, &databuf); + if (!databuf) + return -ENOMEM; + + filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf)); + if (!filename) + return -ENOENT; + + err = mrq_debug_read(bpmp, filename, databuf, len, &nbytes); + if (!err) + seq_commit(m, nbytes); + + return err; +} + +static ssize_t bpmp_debug_store(struct file *file, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct inode *inode = file_inode(file); + struct tegra_bpmp *bpmp = inode->i_private; + char *databuf = NULL; + char fnamebuf[256]; + const char *filename; + ssize_t err; + + filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf)); + if (!filename) + return -ENOENT; + + databuf = kmalloc(count, GFP_KERNEL); + if (!databuf) + return -ENOMEM; + + if (copy_from_user(databuf, buf, count)) { + err = -EFAULT; + goto free_ret; + } + + err = mrq_debug_write(bpmp, filename, databuf, count); + +free_ret: + kfree(databuf); + + return err ?: count; +} + +static int bpmp_debug_open(struct inode *inode, struct file *file) +{ + return single_open_size(file, bpmp_debug_show, file, SZ_256K); +} + +static const struct file_operations bpmp_debug_fops = { + .open = bpmp_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .write = bpmp_debug_store, + .release = single_release, +}; + +static int bpmp_populate_debugfs_inband(struct tegra_bpmp *bpmp, + struct dentry *parent, + char *ppath) +{ + const size_t pathlen = SZ_256; + const size_t bufsize = SZ_16K; + uint32_t dsize, attrs = 0; + struct dentry *dentry; + struct seqbuf seqbuf; + char *buf, *pathbuf; + const char *name; + int err = 0; + + if (!bpmp || !parent || !ppath) + return -EINVAL; + + buf = kmalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pathbuf = kzalloc(pathlen, GFP_KERNEL); + if (!pathbuf) { + kfree(buf); + return -ENOMEM; + } + + err = mrq_debug_read(bpmp, ppath, buf, bufsize, &dsize); + if (err) + goto out; + + seqbuf_init(&seqbuf, buf, dsize); + + while (!seqbuf_eof(&seqbuf)) { + err = seqbuf_read_u32(&seqbuf, &attrs); + if (err) + goto out; + + err = seqbuf_read_str(&seqbuf, &name); + if (err < 0) + goto out; + + if (attrs & DEBUGFS_S_ISDIR) { + size_t len; + + dentry = debugfs_create_dir(name, parent); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + goto out; + } + + len = strlen(ppath) + strlen(name) + 1; + if (len >= pathlen) { + err = -EINVAL; + goto out; + } + + strncpy(pathbuf, ppath, pathlen); + strncat(pathbuf, name, strlen(name)); + strcat(pathbuf, "/"); + + err = bpmp_populate_debugfs_inband(bpmp, dentry, + pathbuf); + if (err < 0) + goto out; + } else { + umode_t mode; + + mode = attrs & DEBUGFS_S_IRUSR ? 0400 : 0; + mode |= attrs & DEBUGFS_S_IWUSR ? 0200 : 0; + dentry = debugfs_create_file(name, mode, parent, bpmp, + &bpmp_debug_fops); + if (!dentry) { + err = -ENOMEM; + goto out; + } + } + } + +out: + kfree(pathbuf); + kfree(buf); + + return err; +} + static int mrq_debugfs_read(struct tegra_bpmp *bpmp, dma_addr_t name, size_t sz_name, dma_addr_t data, size_t sz_data, @@ -127,6 +478,8 @@ static int mrq_debugfs_read(struct tegra_bpmp *bpmp, err = tegra_bpmp_transfer(bpmp, &msg); if (err < 0) return err; + else if (msg.rx.ret < 0) + return -EINVAL; *nbytes = (size_t)resp.fop.nbytes; @@ -184,6 +537,8 @@ static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr, err = tegra_bpmp_transfer(bpmp, &msg); if (err < 0) return err; + else if (msg.rx.ret < 0) + return -EINVAL; *nbytes = (size_t)resp.dumpdir.nbytes; @@ -202,7 +557,7 @@ static int debugfs_show(struct seq_file *m, void *p) char buf[256]; const char *filename; size_t len, nbytes; - int ret; + int err; filename = get_filename(bpmp, file, buf, sizeof(buf)); if (!filename) @@ -216,24 +571,24 @@ static int debugfs_show(struct seq_file *m, void *p) datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys, GFP_KERNEL | GFP_DMA32); if (!datavirt) { - ret = -ENOMEM; + err = -ENOMEM; goto free_namebuf; } len = strlen(filename); strncpy(namevirt, filename, namesize); - ret = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize, + err = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize, &nbytes); - if (!ret) + if (!err) seq_write(m, datavirt, nbytes); dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys); free_namebuf: dma_free_coherent(bpmp->dev, namesize, namevirt, namephys); - return ret; + return err; } static int debugfs_open(struct inode *inode, struct file *file) @@ -253,7 +608,7 @@ static ssize_t debugfs_store(struct file *file, const char __user *buf, char fnamebuf[256]; const char *filename; size_t len; - int ret; + int err; filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf)); if (!filename) @@ -267,7 +622,7 @@ static ssize_t debugfs_store(struct file *file, const char __user *buf, datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys, GFP_KERNEL | GFP_DMA32); if (!datavirt) { - ret = -ENOMEM; + err = -ENOMEM; goto free_namebuf; } @@ -275,11 +630,11 @@ static ssize_t debugfs_store(struct file *file, const char __user *buf, strncpy(namevirt, filename, namesize); if (copy_from_user(datavirt, buf, count)) { - ret = -EFAULT; + err = -EFAULT; goto free_databuf; } - ret = mrq_debugfs_write(bpmp, namephys, len, dataphys, + err = mrq_debugfs_write(bpmp, namephys, len, dataphys, count); free_databuf: @@ -287,7 +642,7 @@ free_databuf: free_namebuf: dma_free_coherent(bpmp->dev, namesize, namevirt, namephys); - return ret ?: count; + return err ?: count; } static const struct file_operations debugfs_fops = { @@ -350,59 +705,66 @@ static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf, return 0; } -static int create_debugfs_mirror(struct tegra_bpmp *bpmp, void *buf, - size_t bufsize, struct dentry *root) +static int bpmp_populate_debugfs_shmem(struct tegra_bpmp *bpmp) { struct seqbuf seqbuf; + const size_t sz = SZ_512K; + dma_addr_t phys; + size_t nbytes; + void *virt; int err; - bpmp->debugfs_mirror = debugfs_create_dir("debug", root); - if (!bpmp->debugfs_mirror) + virt = dma_alloc_coherent(bpmp->dev, sz, &phys, + GFP_KERNEL | GFP_DMA32); + if (!virt) return -ENOMEM; - seqbuf_init(&seqbuf, buf, bufsize); - err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0); + err = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes); if (err < 0) { - debugfs_remove_recursive(bpmp->debugfs_mirror); - bpmp->debugfs_mirror = NULL; + goto free; + } else if (nbytes > sz) { + err = -EINVAL; + goto free; } + seqbuf_init(&seqbuf, virt, nbytes); + err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0); +free: + dma_free_coherent(bpmp->dev, sz, virt, phys); + return err; } int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) { - dma_addr_t phys; - void *virt; - const size_t sz = SZ_256K; - size_t nbytes; - int ret; struct dentry *root; + bool inband; + int err; - if (!tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS)) + inband = tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUG); + + if (!inband && !tegra_bpmp_mrq_is_supported(bpmp, MRQ_DEBUGFS)) return 0; root = debugfs_create_dir("bpmp", NULL); if (!root) return -ENOMEM; - virt = dma_alloc_coherent(bpmp->dev, sz, &phys, - GFP_KERNEL | GFP_DMA32); - if (!virt) { - ret = -ENOMEM; + bpmp->debugfs_mirror = debugfs_create_dir("debug", root); + if (!bpmp->debugfs_mirror) { + err = -ENOMEM; goto out; } - ret = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes); - if (ret < 0) - goto free; + if (inband) + err = bpmp_populate_debugfs_inband(bpmp, bpmp->debugfs_mirror, + "/"); + else + err = bpmp_populate_debugfs_shmem(bpmp); - ret = create_debugfs_mirror(bpmp, virt, nbytes, root); -free: - dma_free_coherent(bpmp->dev, sz, virt, phys); out: - if (ret < 0) - debugfs_remove(root); + if (err < 0) + debugfs_remove_recursive(root); - return ret; + return err; } diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index fe6702df24bf..4d93d8925e14 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -515,10 +515,10 @@ bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq) .size = sizeof(resp), }, }; - int ret; + int err; - ret = tegra_bpmp_transfer(bpmp, &msg); - if (ret || msg.rx.ret) + err = tegra_bpmp_transfer(bpmp, &msg); + if (err || msg.rx.ret) return false; return resp.status == 0; diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 4126be9e3216..53cee17d0115 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -2,7 +2,7 @@ /* * Texas Instruments System Control Interface Protocol Driver * - * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ * Nishanth Menon */ diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index f0d068c03944..57cd04062994 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -6,7 +6,7 @@ * The system works in a message response protocol * See: http://processors.wiki.ti.com/index.php/TISCI for details * - * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef __TI_SCI_H diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c index e27f68437b56..50bb2a6d6ccf 100644 --- a/drivers/firmware/turris-mox-rwtm.c +++ b/drivers/firmware/turris-mox-rwtm.c @@ -7,6 +7,7 @@ #include <linux/armada-37xx-rwtm-mailbox.h> #include <linux/completion.h> +#include <linux/debugfs.h> #include <linux/dma-mapping.h> #include <linux/hw_random.h> #include <linux/mailbox_client.h> @@ -69,6 +70,18 @@ struct mox_rwtm { /* public key burned in eFuse */ int has_pubkey; u8 pubkey[135]; + +#ifdef CONFIG_DEBUG_FS + /* + * Signature process. This is currently done via debugfs, because it + * does not conform to the sysfs standard "one file per attribute". + * It should be rewritten via crypto API once akcipher API is available + * from userspace. + */ + struct dentry *debugfs_root; + u32 last_sig[34]; + int last_sig_done; +#endif }; struct mox_kobject { @@ -279,6 +292,152 @@ unlock_mutex: return ret; } +#ifdef CONFIG_DEBUG_FS +static int rwtm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return nonseekable_open(inode, file); +} + +static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct mox_rwtm *rwtm = file->private_data; + ssize_t ret; + + /* only allow one read, of 136 bytes, from position 0 */ + if (*ppos != 0) + return 0; + + if (len < 136) + return -EINVAL; + + if (!rwtm->last_sig_done) + return -ENODATA; + + /* 2 arrays of 17 32-bit words are 136 bytes */ + ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig, 136); + rwtm->last_sig_done = 0; + + return ret; +} + +static ssize_t do_sign_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct mox_rwtm *rwtm = file->private_data; + struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply; + struct armada_37xx_rwtm_tx_msg msg; + loff_t dummy = 0; + ssize_t ret; + + /* the input is a SHA-512 hash, so exactly 64 bytes have to be read */ + if (len != 64) + return -EINVAL; + + /* if last result is not zero user has not read that information yet */ + if (rwtm->last_sig_done) + return -EBUSY; + + if (!mutex_trylock(&rwtm->busy)) + return -EBUSY; + + /* + * Here we have to send: + * 1. Address of the input to sign. + * The input is an array of 17 32-bit words, the first (most + * significat) is 0, the rest 16 words are copied from the SHA-512 + * hash given by the user and converted from BE to LE. + * 2. Address of the buffer where ECDSA signature value R shall be + * stored by the rWTM firmware. + * 3. Address of the buffer where ECDSA signature value S shall be + * stored by the rWTM firmware. + */ + memset(rwtm->buf, 0, 4); + ret = simple_write_to_buffer(rwtm->buf + 4, 64, &dummy, buf, len); + if (ret < 0) + goto unlock_mutex; + be32_to_cpu_array(rwtm->buf, rwtm->buf, 17); + + msg.command = MBOX_CMD_SIGN; + msg.args[0] = 1; + msg.args[1] = rwtm->buf_phys; + msg.args[2] = rwtm->buf_phys + 68; + msg.args[3] = rwtm->buf_phys + 2 * 68; + ret = mbox_send_message(rwtm->mbox, &msg); + if (ret < 0) + goto unlock_mutex; + + ret = wait_for_completion_interruptible(&rwtm->cmd_done); + if (ret < 0) + goto unlock_mutex; + + ret = MBOX_STS_VALUE(reply->retval); + if (MBOX_STS_ERROR(reply->retval) != MBOX_STS_SUCCESS) + goto unlock_mutex; + + /* + * Here we read the R and S values of the ECDSA signature + * computed by the rWTM firmware and convert their words from + * LE to BE. + */ + memcpy(rwtm->last_sig, rwtm->buf + 68, 136); + cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig, 34); + rwtm->last_sig_done = 1; + + mutex_unlock(&rwtm->busy); + return len; +unlock_mutex: + mutex_unlock(&rwtm->busy); + return ret; +} + +static const struct file_operations do_sign_fops = { + .owner = THIS_MODULE, + .open = rwtm_debug_open, + .read = do_sign_read, + .write = do_sign_write, + .llseek = no_llseek, +}; + +static int rwtm_register_debugfs(struct mox_rwtm *rwtm) +{ + struct dentry *root, *entry; + + root = debugfs_create_dir("turris-mox-rwtm", NULL); + + if (IS_ERR(root)) + return PTR_ERR(root); + + entry = debugfs_create_file_unsafe("do_sign", 0600, root, rwtm, + &do_sign_fops); + if (IS_ERR(entry)) + goto err_remove; + + rwtm->debugfs_root = root; + + return 0; +err_remove: + debugfs_remove_recursive(root); + return PTR_ERR(entry); +} + +static void rwtm_unregister_debugfs(struct mox_rwtm *rwtm) +{ + debugfs_remove_recursive(rwtm->debugfs_root); +} +#else +static inline int rwtm_register_debugfs(struct mox_rwtm *rwtm) +{ + return 0; +} + +static inline void rwtm_unregister_debugfs(struct mox_rwtm *rwtm) +{ +} +#endif + static int turris_mox_rwtm_probe(struct platform_device *pdev) { struct mox_rwtm *rwtm; @@ -340,6 +499,12 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev) goto free_channel; } + ret = rwtm_register_debugfs(rwtm); + if (ret < 0) { + dev_err(dev, "Failed creating debugfs entries: %i\n", ret); + goto free_channel; + } + return 0; free_channel: @@ -355,6 +520,7 @@ static int turris_mox_rwtm_remove(struct platform_device *pdev) { struct mox_rwtm *rwtm = platform_get_drvdata(pdev); + rwtm_unregister_debugfs(rwtm); sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs); kobject_put(rwtm_to_kobj(rwtm)); mbox_free_channel(rwtm->mbox); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c index 775e389c9a13..16596a9ccabe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c @@ -696,7 +696,7 @@ static ssize_t amdgpu_set_pp_table(struct device *dev, * default power levels, write "r" (reset) to the file to reset them. * * - * < For Vega20 > + * < For Vega20 and newer ASICs > * * Reading the file will display: * @@ -1668,7 +1668,7 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev, } /** - * DOC: busy_percent + * DOC: gpu_busy_percent * * The amdgpu driver provides a sysfs API for reading how busy the GPU * is as a percentage. The file gpu_busy_percent is used for this. diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index f0587d94294d..fee60921fccf 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -40,6 +40,7 @@ #include <drm/drm_file.h> #include <drm/drm_drv.h> #include <drm/drm_device.h> +#include <drm/drm_ioctl.h> #include <kgd_kfd_interface.h> #include <linux/swap.h> @@ -1076,7 +1077,7 @@ static inline int kfd_devcgroup_check_permission(struct kfd_dev *kfd) #if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) struct drm_device *ddev = kfd->ddev; - return devcgroup_check_permission(DEVCG_DEV_CHAR, ddev->driver->major, + return devcgroup_check_permission(DEVCG_DEV_CHAR, DRM_MAJOR, ddev->render->index, DEVCG_ACC_WRITE | DEVCG_ACC_READ); #else diff --git a/drivers/gpu/drm/amd/display/dc/dsc/Makefile b/drivers/gpu/drm/amd/display/dc/dsc/Makefile index 3f66868df171..ea29cf95d470 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dsc/Makefile @@ -28,8 +28,6 @@ endif endif CFLAGS_$(AMDDALPATH)/dc/dsc/rc_calc.o := $(dsc_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dsc/rc_calc_dpi.o := $(dsc_ccflags) -CFLAGS_$(AMDDALPATH)/dc/dsc/dc_dsc.o := $(dsc_ccflags) DSC = dc_dsc.o rc_calc.o rc_calc_dpi.o diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c index 0ea6662a1563..0c7f247bb7de 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c @@ -22,10 +22,12 @@ * Author: AMD */ +#include <drm/drm_dsc.h> #include "dc_hw_types.h" #include "dsc.h" #include <drm/drm_dp_helper.h> #include "dc.h" +#include "rc_calc.h" /* This module's internal functions */ @@ -304,22 +306,6 @@ static inline uint32_t dsc_div_by_10_round_up(uint32_t value) return (value + 9) / 10; } -static inline uint32_t calc_dsc_bpp_x16(uint32_t stream_bandwidth_kbps, uint32_t pix_clk_100hz, uint32_t bpp_increment_div) -{ - uint32_t dsc_target_bpp_x16; - float f_dsc_target_bpp; - float f_stream_bandwidth_100bps = stream_bandwidth_kbps * 10.0f; - uint32_t precision = bpp_increment_div; // bpp_increment_div is actually precision - - f_dsc_target_bpp = f_stream_bandwidth_100bps / pix_clk_100hz; - - // Round down to the nearest precision stop to bring it into DSC spec range - dsc_target_bpp_x16 = (uint32_t)(f_dsc_target_bpp * precision); - dsc_target_bpp_x16 = (dsc_target_bpp_x16 * 16) / precision; - - return dsc_target_bpp_x16; -} - /* Get DSC bandwidth range based on [min_bpp, max_bpp] target bitrate range, and timing's pixel clock * and uncompressed bandwidth. */ diff --git a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c index 03ae15946c6d..667afbc260f9 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.c @@ -23,6 +23,7 @@ * Authors: AMD * */ +#include <drm/drm_dsc.h> #include "os_types.h" #include "rc_calc.h" @@ -40,7 +41,8 @@ break -void get_qp_set(qp_set qps, enum colour_mode cm, enum bits_per_comp bpc, enum max_min max_min, float bpp) +static void get_qp_set(qp_set qps, enum colour_mode cm, enum bits_per_comp bpc, + enum max_min max_min, float bpp) { int mode = MODE_SELECT(444, 422, 420); int sel = table_hash(mode, bpc, max_min); @@ -85,7 +87,7 @@ void get_qp_set(qp_set qps, enum colour_mode cm, enum bits_per_comp bpc, enum ma memcpy(qps, table[index].qps, sizeof(qp_set)); } -double dsc_roundf(double num) +static double dsc_roundf(double num) { if (num < 0.0) num = num - 0.5; @@ -95,7 +97,7 @@ double dsc_roundf(double num) return (int)(num); } -double dsc_ceil(double num) +static double dsc_ceil(double num) { double retval = (int)num; @@ -105,7 +107,7 @@ double dsc_ceil(double num) return (int)retval; } -void get_ofs_set(qp_set ofs, enum colour_mode mode, float bpp) +static void get_ofs_set(qp_set ofs, enum colour_mode mode, float bpp) { int *p = ofs; @@ -160,7 +162,7 @@ void get_ofs_set(qp_set ofs, enum colour_mode mode, float bpp) } } -int median3(int a, int b, int c) +static int median3(int a, int b, int c) { if (a > b) swap(a, b); @@ -172,13 +174,25 @@ int median3(int a, int b, int c) return b; } -void calc_rc_params(struct rc_params *rc, enum colour_mode cm, enum bits_per_comp bpc, float bpp, int slice_width, int slice_height, int minor_version) +static void _do_calc_rc_params(struct rc_params *rc, enum colour_mode cm, + enum bits_per_comp bpc, u8 drm_bpp, + bool is_navite_422_or_420, + int slice_width, int slice_height, + int minor_version) { + float bpp; float bpp_group; float initial_xmit_delay_factor; int padding_pixels; int i; + bpp = ((float)drm_bpp / 16.0); + /* in native_422 or native_420 modes, the bits_per_pixel is double the + * target bpp (the latter is what calc_rc_params expects) + */ + if (is_navite_422_or_420) + bpp /= 2.0; + rc->rc_quant_incr_limit0 = ((bpc == BPC_8) ? 11 : (bpc == BPC_10 ? 15 : 19)) - ((minor_version == 1 && cm == CM_444) ? 1 : 0); rc->rc_quant_incr_limit1 = ((bpc == BPC_8) ? 11 : (bpc == BPC_10 ? 15 : 19)) - ((minor_version == 1 && cm == CM_444) ? 1 : 0); @@ -251,3 +265,128 @@ void calc_rc_params(struct rc_params *rc, enum colour_mode cm, enum bits_per_com rc->rc_buf_thresh[13] = 8064; } +static u32 _do_bytes_per_pixel_calc(int slice_width, u8 drm_bpp, + bool is_navite_422_or_420) +{ + float bpp; + u32 bytes_per_pixel; + double d_bytes_per_pixel; + + bpp = ((float)drm_bpp / 16.0); + d_bytes_per_pixel = dsc_ceil(bpp * slice_width / 8.0) / slice_width; + // TODO: Make sure the formula for calculating this is precise (ceiling + // vs. floor, and at what point they should be applied) + if (is_navite_422_or_420) + d_bytes_per_pixel /= 2; + + bytes_per_pixel = (u32)dsc_ceil(d_bytes_per_pixel * 0x10000000); + + return bytes_per_pixel; +} + +static u32 _do_calc_dsc_bpp_x16(u32 stream_bandwidth_kbps, u32 pix_clk_100hz, + u32 bpp_increment_div) +{ + u32 dsc_target_bpp_x16; + float f_dsc_target_bpp; + float f_stream_bandwidth_100bps; + // bpp_increment_div is actually precision + u32 precision = bpp_increment_div; + + f_stream_bandwidth_100bps = stream_bandwidth_kbps * 10.0f; + f_dsc_target_bpp = f_stream_bandwidth_100bps / pix_clk_100hz; + + // Round down to the nearest precision stop to bring it into DSC spec + // range + dsc_target_bpp_x16 = (u32)(f_dsc_target_bpp * precision); + dsc_target_bpp_x16 = (dsc_target_bpp_x16 * 16) / precision; + + return dsc_target_bpp_x16; +} + +/** + * calc_rc_params - reads the user's cmdline mode + * @rc: DC internal DSC parameters + * @pps: DRM struct with all required DSC values + * + * This function expects a drm_dsc_config data struct with all the required DSC + * values previously filled out by our driver and based on this information it + * computes some of the DSC values. + * + * @note This calculation requires float point operation, most of it executes + * under kernel_fpu_{begin,end}. + */ +void calc_rc_params(struct rc_params *rc, const struct drm_dsc_config *pps) +{ + enum colour_mode mode; + enum bits_per_comp bpc; + bool is_navite_422_or_420; + u8 drm_bpp = pps->bits_per_pixel; + int slice_width = pps->slice_width; + int slice_height = pps->slice_height; + + mode = pps->convert_rgb ? CM_RGB : (pps->simple_422 ? CM_444 : + (pps->native_422 ? CM_422 : + pps->native_420 ? CM_420 : CM_444)); + bpc = (pps->bits_per_component == 8) ? BPC_8 : (pps->bits_per_component == 10) + ? BPC_10 : BPC_12; + + is_navite_422_or_420 = pps->native_422 || pps->native_420; + + DC_FP_START(); + _do_calc_rc_params(rc, mode, bpc, drm_bpp, is_navite_422_or_420, + slice_width, slice_height, + pps->dsc_version_minor); + DC_FP_END(); +} + +/** + * calc_dsc_bytes_per_pixel - calculate bytes per pixel + * @pps: DRM struct with all required DSC values + * + * Based on the information inside drm_dsc_config, this function calculates the + * total of bytes per pixel. + * + * @note This calculation requires float point operation, most of it executes + * under kernel_fpu_{begin,end}. + * + * Return: + * Return the number of bytes per pixel + */ +u32 calc_dsc_bytes_per_pixel(const struct drm_dsc_config *pps) + +{ + u32 ret; + u8 drm_bpp = pps->bits_per_pixel; + int slice_width = pps->slice_width; + bool is_navite_422_or_420 = pps->native_422 || pps->native_420; + + DC_FP_START(); + ret = _do_bytes_per_pixel_calc(slice_width, drm_bpp, + is_navite_422_or_420); + DC_FP_END(); + return ret; +} + +/** + * calc_dsc_bpp_x16 - retrieve the dsc bits per pixel + * @stream_bandwidth_kbps: + * @pix_clk_100hz: + * @bpp_increment_div: + * + * Calculate the total of bits per pixel for DSC configuration. + * + * @note This calculation requires float point operation, most of it executes + * under kernel_fpu_{begin,end}. + */ +u32 calc_dsc_bpp_x16(u32 stream_bandwidth_kbps, u32 pix_clk_100hz, + u32 bpp_increment_div) +{ + u32 dsc_bpp; + + DC_FP_START(); + dsc_bpp = _do_calc_dsc_bpp_x16(stream_bandwidth_kbps, pix_clk_100hz, + bpp_increment_div); + DC_FP_END(); + return dsc_bpp; +} diff --git a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.h b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.h index b6b1f09c2009..21723fa6561e 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.h +++ b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc.h @@ -77,7 +77,10 @@ struct qp_entry { typedef struct qp_entry qp_table[]; -void calc_rc_params(struct rc_params *rc, enum colour_mode cm, enum bits_per_comp bpc, float bpp, int slice_width, int slice_height, int minor_version); +void calc_rc_params(struct rc_params *rc, const struct drm_dsc_config *pps); +u32 calc_dsc_bytes_per_pixel(const struct drm_dsc_config *pps); +u32 calc_dsc_bpp_x16(u32 stream_bandwidth_kbps, u32 pix_clk_100hz, + u32 bpp_increment_div); #endif diff --git a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c index 1f6e63b71456..ef830aded5b1 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c +++ b/drivers/gpu/drm/amd/display/dc/dsc/rc_calc_dpi.c @@ -27,8 +27,6 @@ #include "dscc_types.h" #include "rc_calc.h" -double dsc_ceil(double num); - static void copy_pps_fields(struct drm_dsc_config *to, const struct drm_dsc_config *from) { to->line_buf_depth = from->line_buf_depth; @@ -100,34 +98,13 @@ static void copy_rc_to_cfg(struct drm_dsc_config *dsc_cfg, const struct rc_param int dscc_compute_dsc_parameters(const struct drm_dsc_config *pps, struct dsc_parameters *dsc_params) { - enum colour_mode mode = pps->convert_rgb ? CM_RGB : - (pps->simple_422 ? CM_444 : - (pps->native_422 ? CM_422 : - pps->native_420 ? CM_420 : CM_444)); - enum bits_per_comp bpc = (pps->bits_per_component == 8) ? BPC_8 : - (pps->bits_per_component == 10) ? BPC_10 : BPC_12; - float bpp = ((float) pps->bits_per_pixel / 16.0); - int slice_width = pps->slice_width; - int slice_height = pps->slice_height; int ret; struct rc_params rc; struct drm_dsc_config dsc_cfg; - double d_bytes_per_pixel = dsc_ceil(bpp * slice_width / 8.0) / slice_width; - - // TODO: Make sure the formula for calculating this is precise (ceiling vs. floor, and at what point they should be applied) - if (pps->native_422 || pps->native_420) - d_bytes_per_pixel /= 2; - - dsc_params->bytes_per_pixel = (uint32_t)dsc_ceil(d_bytes_per_pixel * 0x10000000); - - /* in native_422 or native_420 modes, the bits_per_pixel is double the target bpp - * (the latter is what calc_rc_params expects) - */ - if (pps->native_422 || pps->native_420) - bpp /= 2.0; + dsc_params->bytes_per_pixel = calc_dsc_bytes_per_pixel(pps); - calc_rc_params(&rc, mode, bpc, bpp, slice_width, slice_height, pps->dsc_version_minor); + calc_rc_params(&rc, pps); dsc_params->pps = *pps; dsc_params->pps.initial_scale_value = 8 * rc.rc_model_size / (rc.rc_model_size - rc.initial_fullness_offset); diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c index 9431b48aecb4..bcfe34ef8c28 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c @@ -843,7 +843,7 @@ static bool build_regamma(struct pwl_float_data_ex *rgb_regamma, pow_buffer_ptr = -1; // reset back to no optimize ret = true; release: - kfree(coeff); + kvfree(coeff); return ret; } @@ -1777,7 +1777,7 @@ bool calculate_user_regamma_ramp(struct dc_transfer_func *output_tf, kfree(rgb_regamma); rgb_regamma_alloc_fail: - kvfree(rgb_user); + kfree(rgb_user); rgb_user_alloc_fail: return ret; } diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/ci_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/ci_smumgr.c index 85e5b1ed22c2..56923a96b450 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/ci_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/ci_smumgr.c @@ -239,7 +239,7 @@ static void ci_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr) switch (dev_id) { case 0x67BA: - case 0x66B1: + case 0x67B1: smu_data->power_tune_defaults = &defaults_hawaii_pro; break; case 0x67B8: diff --git a/drivers/gpu/drm/drm_encoder_slave.c b/drivers/gpu/drm/drm_encoder_slave.c index cf804389f5ec..e464429d32df 100644 --- a/drivers/gpu/drm/drm_encoder_slave.c +++ b/drivers/gpu/drm/drm_encoder_slave.c @@ -61,13 +61,8 @@ int drm_i2c_encoder_init(struct drm_device *dev, request_module("%s%s", I2C_MODULE_PREFIX, info->type); - client = i2c_new_device(adap, info); - if (!client) { - err = -ENOMEM; - goto fail; - } - - if (!client->dev.driver) { + client = i2c_new_client_device(adap, info); + if (!i2c_client_has_driver(client)) { err = -ENODEV; goto fail_unregister; } @@ -84,7 +79,7 @@ int drm_i2c_encoder_init(struct drm_device *dev, err = encoder_drv->encoder_init(client, dev, encoder); if (err) - goto fail_unregister; + goto fail_module_put; if (info->platform_data) encoder->slave_funcs->set_config(&encoder->base, @@ -92,10 +87,10 @@ int drm_i2c_encoder_init(struct drm_device *dev, return 0; +fail_module_put: + module_put(module); fail_unregister: i2c_unregister_device(client); - module_put(module); -fail: return err; } EXPORT_SYMBOL(drm_i2c_encoder_init); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index aa22465bb56e..0575a1eea2a1 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -2579,14 +2579,14 @@ static void icl_ddi_vswing_sequence(struct intel_encoder *encoder, static void tgl_dkl_phy_ddi_vswing_sequence(struct intel_encoder *encoder, int link_clock, - u32 level) + u32 level, enum intel_output_type type) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum tc_port tc_port = intel_port_to_tc(dev_priv, encoder->port); const struct tgl_dkl_phy_ddi_buf_trans *ddi_translations; u32 n_entries, val, ln, dpcnt_mask, dpcnt_val; - if (encoder->type == INTEL_OUTPUT_HDMI) { + if (type == INTEL_OUTPUT_HDMI) { n_entries = ARRAY_SIZE(tgl_dkl_phy_hdmi_ddi_trans); ddi_translations = tgl_dkl_phy_hdmi_ddi_trans; } else { @@ -2638,7 +2638,7 @@ static void tgl_ddi_vswing_sequence(struct intel_encoder *encoder, if (intel_phy_is_combo(dev_priv, phy)) icl_combo_phy_ddi_vswing_sequence(encoder, level, type); else - tgl_dkl_phy_ddi_vswing_sequence(encoder, link_clock, level); + tgl_dkl_phy_ddi_vswing_sequence(encoder, link_clock, level, type); } static u32 translate_signal_level(struct intel_dp *intel_dp, int signal_levels) @@ -2987,7 +2987,7 @@ icl_program_mg_dp_mode(struct intel_digital_port *intel_dig_port, ln1 = intel_de_read(dev_priv, MG_DP_MODE(1, tc_port)); } - ln0 &= ~(MG_DP_MODE_CFG_DP_X1_MODE | MG_DP_MODE_CFG_DP_X1_MODE); + ln0 &= ~(MG_DP_MODE_CFG_DP_X1_MODE | MG_DP_MODE_CFG_DP_X2_MODE); ln1 &= ~(MG_DP_MODE_CFG_DP_X1_MODE | MG_DP_MODE_CFG_DP_X2_MODE); /* DPPATC */ @@ -3472,7 +3472,9 @@ static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, INTEL_OUTPUT_DP_MST); enum phy phy = intel_port_to_phy(dev_priv, encoder->port); - intel_dp_set_infoframes(encoder, false, old_crtc_state, old_conn_state); + if (!is_mst) + intel_dp_set_infoframes(encoder, false, + old_crtc_state, old_conn_state); /* * Power down sink before disabling the port, otherwise we end diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index d18b406f2a7d..f29e51ce489c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -397,6 +397,14 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, */ drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, false); + + /* + * BSpec 4287: disable DIP after the transcoder is disabled and before + * the transcoder clock select is set to none. + */ + if (last_mst_stream) + intel_dp_set_infoframes(&intel_dig_port->base, false, + old_crtc_state, NULL); /* * From TGL spec: "If multi-stream slave transcoder: Configure * Transcoder Clock Select to direct no clock to the transcoder" diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c index da5b61085257..8691eb61e185 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c @@ -646,7 +646,7 @@ static int engine_setup_common(struct intel_engine_cs *engine) struct measure_breadcrumb { struct i915_request rq; struct intel_ring ring; - u32 cs[1024]; + u32 cs[2048]; }; static int measure_breadcrumb_dw(struct intel_context *ce) @@ -668,6 +668,8 @@ static int measure_breadcrumb_dw(struct intel_context *ce) frame->ring.vaddr = frame->cs; frame->ring.size = sizeof(frame->cs); + frame->ring.wrap = + BITS_PER_TYPE(frame->ring.size) - ilog2(frame->ring.size); frame->ring.effective_size = frame->ring.size; intel_ring_update_space(&frame->ring); frame->rq.ring = &frame->ring; diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c index 87e6c5bdd2dc..7c3d8ef4a47c 100644 --- a/drivers/gpu/drm/i915/gt/intel_lrc.c +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c @@ -1134,6 +1134,13 @@ __unwind_incomplete_requests(struct intel_engine_cs *engine) list_move(&rq->sched.link, pl); set_bit(I915_FENCE_FLAG_PQUEUE, &rq->fence.flags); + /* Check in case we rollback so far we wrap [size/2] */ + if (intel_ring_direction(rq->ring, + intel_ring_wrap(rq->ring, + rq->tail), + rq->ring->tail) > 0) + rq->context->lrc.desc |= CTX_DESC_FORCE_RESTORE; + active = rq; } else { struct intel_engine_cs *owner = rq->context->engine; @@ -1498,8 +1505,9 @@ static u64 execlists_update_context(struct i915_request *rq) * HW has a tendency to ignore us rewinding the TAIL to the end of * an earlier request. */ + GEM_BUG_ON(ce->lrc_reg_state[CTX_RING_TAIL] != rq->ring->tail); + prev = rq->ring->tail; tail = intel_ring_set_tail(rq->ring, rq->tail); - prev = ce->lrc_reg_state[CTX_RING_TAIL]; if (unlikely(intel_ring_direction(rq->ring, tail, prev) <= 0)) desc |= CTX_DESC_FORCE_RESTORE; ce->lrc_reg_state[CTX_RING_TAIL] = tail; @@ -1895,7 +1903,8 @@ static void defer_active(struct intel_engine_cs *engine) static bool need_timeslice(const struct intel_engine_cs *engine, - const struct i915_request *rq) + const struct i915_request *rq, + const struct rb_node *rb) { int hint; @@ -1903,9 +1912,28 @@ need_timeslice(const struct intel_engine_cs *engine, return false; hint = engine->execlists.queue_priority_hint; + + if (rb) { + const struct virtual_engine *ve = + rb_entry(rb, typeof(*ve), nodes[engine->id].rb); + const struct intel_engine_cs *inflight = + intel_context_inflight(&ve->context); + + if (!inflight || inflight == engine) { + struct i915_request *next; + + rcu_read_lock(); + next = READ_ONCE(ve->request); + if (next) + hint = max(hint, rq_prio(next)); + rcu_read_unlock(); + } + } + if (!list_is_last(&rq->sched.link, &engine->active.requests)) hint = max(hint, rq_prio(list_next_entry(rq, sched.link))); + GEM_BUG_ON(hint >= I915_PRIORITY_UNPREEMPTABLE); return hint >= effective_prio(rq); } @@ -1977,10 +2005,9 @@ static void set_timeslice(struct intel_engine_cs *engine) set_timer_ms(&engine->execlists.timer, duration); } -static void start_timeslice(struct intel_engine_cs *engine) +static void start_timeslice(struct intel_engine_cs *engine, int prio) { struct intel_engine_execlists *execlists = &engine->execlists; - const int prio = queue_prio(execlists); unsigned long duration; if (!intel_engine_has_timeslices(engine)) @@ -2140,7 +2167,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) __unwind_incomplete_requests(engine); last = NULL; - } else if (need_timeslice(engine, last) && + } else if (need_timeslice(engine, last, rb) && timeslice_expired(execlists, last)) { if (i915_request_completed(last)) { tasklet_hi_schedule(&execlists->tasklet); @@ -2188,7 +2215,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) * Even if ELSP[1] is occupied and not worthy * of timeslices, our queue might be. */ - start_timeslice(engine); + start_timeslice(engine, queue_prio(execlists)); return; } } @@ -2223,7 +2250,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) if (last && !can_merge_rq(last, rq)) { spin_unlock(&ve->base.active.lock); - start_timeslice(engine); + start_timeslice(engine, rq_prio(rq)); return; /* leave this for another sibling */ } @@ -4739,6 +4766,14 @@ static int gen12_emit_flush(struct i915_request *request, u32 mode) return 0; } +static void assert_request_valid(struct i915_request *rq) +{ + struct intel_ring *ring __maybe_unused = rq->ring; + + /* Can we unwind this request without appearing to go forwards? */ + GEM_BUG_ON(intel_ring_direction(ring, rq->wa_tail, rq->head) <= 0); +} + /* * Reserve space for 2 NOOPs at the end of each request to be * used as a workaround for not being allowed to do lite @@ -4751,6 +4786,9 @@ static u32 *gen8_emit_wa_tail(struct i915_request *request, u32 *cs) *cs++ = MI_NOOP; request->wa_tail = intel_ring_offset(request, cs); + /* Check that entire request is less than half the ring */ + assert_request_valid(request); + return cs; } diff --git a/drivers/gpu/drm/i915/gt/intel_ring.c b/drivers/gpu/drm/i915/gt/intel_ring.c index 8cda1b7e17ba..bdb324167ef3 100644 --- a/drivers/gpu/drm/i915/gt/intel_ring.c +++ b/drivers/gpu/drm/i915/gt/intel_ring.c @@ -315,3 +315,7 @@ int intel_ring_cacheline_align(struct i915_request *rq) GEM_BUG_ON(rq->ring->emit & (CACHELINE_BYTES - 1)); return 0; } + +#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) +#include "selftest_ring.c" +#endif diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c index 90a2b9e399b0..85d2bef51524 100644 --- a/drivers/gpu/drm/i915/gt/intel_workarounds.c +++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c @@ -179,6 +179,12 @@ wa_write_or(struct i915_wa_list *wal, i915_reg_t reg, u32 set) } static void +wa_write_clr(struct i915_wa_list *wal, i915_reg_t reg, u32 clr) +{ + wa_write_masked_or(wal, reg, clr, 0); +} + +static void wa_masked_en(struct i915_wa_list *wal, i915_reg_t reg, u32 val) { wa_add(wal, reg, 0, _MASKED_BIT_ENABLE(val), val); @@ -687,6 +693,227 @@ int intel_engine_emit_ctx_wa(struct i915_request *rq) } static void +gen4_gt_workarounds_init(struct drm_i915_private *i915, + struct i915_wa_list *wal) +{ + /* WaDisable_RenderCache_OperationalFlush:gen4,ilk */ + wa_masked_dis(wal, CACHE_MODE_0, RC_OP_FLUSH_ENABLE); +} + +static void +g4x_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal) +{ + gen4_gt_workarounds_init(i915, wal); + + /* WaDisableRenderCachePipelinedFlush:g4x,ilk */ + wa_masked_en(wal, CACHE_MODE_0, CM0_PIPELINED_RENDER_FLUSH_DISABLE); +} + +static void +ilk_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal) +{ + g4x_gt_workarounds_init(i915, wal); + + wa_masked_en(wal, _3D_CHICKEN2, _3D_CHICKEN2_WM_READ_PIPELINED); +} + +static void +snb_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal) +{ + /* WaDisableHiZPlanesWhenMSAAEnabled:snb */ + wa_masked_en(wal, + _3D_CHICKEN, + _3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB); + + /* WaDisable_RenderCache_OperationalFlush:snb */ + wa_masked_dis(wal, CACHE_MODE_0, RC_OP_FLUSH_ENABLE); + + /* + * BSpec recommends 8x4 when MSAA is used, + * however in practice 16x4 seems fastest. + * + * Note that PS/WM thread counts depend on the WIZ hashing + * disable bit, which we don't touch here, but it's good + * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). + */ + wa_add(wal, + GEN6_GT_MODE, 0, + _MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4), + GEN6_WIZ_HASHING_16x4); + + wa_masked_dis(wal, CACHE_MODE_0, CM0_STC_EVICT_DISABLE_LRA_SNB); + + wa_masked_en(wal, + _3D_CHICKEN3, + /* WaStripsFansDisableFastClipPerformanceFix:snb */ + _3D_CHICKEN3_SF_DISABLE_FASTCLIP_CULL | + /* + * Bspec says: + * "This bit must be set if 3DSTATE_CLIP clip mode is set + * to normal and 3DSTATE_SF number of SF output attributes + * is more than 16." + */ + _3D_CHICKEN3_SF_DISABLE_PIPELINED_ATTR_FETCH); +} + +static void +ivb_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal) +{ + /* WaDisableEarlyCull:ivb */ + wa_masked_en(wal, _3D_CHICKEN3, _3D_CHICKEN_SF_DISABLE_OBJEND_CULL); + + /* WaDisablePSDDualDispatchEnable:ivb */ + if (IS_IVB_GT1(i915)) + wa_masked_en(wal, + GEN7_HALF_SLICE_CHICKEN1, + GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE); + + /* WaDisable_RenderCache_OperationalFlush:ivb */ + wa_masked_dis(wal, CACHE_MODE_0_GEN7, RC_OP_FLUSH_ENABLE); + + /* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */ + wa_masked_dis(wal, + GEN7_COMMON_SLICE_CHICKEN1, + GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); + + /* WaApplyL3ControlAndL3ChickenMode:ivb */ + wa_write(wal, GEN7_L3CNTLREG1, GEN7_WA_FOR_GEN7_L3_CONTROL); + wa_write(wal, GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE); + + /* WaForceL3Serialization:ivb */ + wa_write_clr(wal, GEN7_L3SQCREG4, L3SQ_URB_READ_CAM_MATCH_DISABLE); + + /* + * WaVSThreadDispatchOverride:ivb,vlv + * + * This actually overrides the dispatch + * mode for all thread types. + */ + wa_write_masked_or(wal, GEN7_FF_THREAD_MODE, + GEN7_FF_SCHED_MASK, + GEN7_FF_TS_SCHED_HW | + GEN7_FF_VS_SCHED_HW | + GEN7_FF_DS_SCHED_HW); + + if (0) { /* causes HiZ corruption on ivb:gt1 */ + /* enable HiZ Raw Stall Optimization */ + wa_masked_dis(wal, CACHE_MODE_0_GEN7, HIZ_RAW_STALL_OPT_DISABLE); + } + + /* WaDisable4x2SubspanOptimization:ivb */ + wa_masked_en(wal, CACHE_MODE_1, PIXEL_SUBSPAN_COLLECT_OPT_DISABLE); + + /* + * BSpec recommends 8x4 when MSAA is used, + * however in practice 16x4 seems fastest. + * + * Note that PS/WM thread counts depend on the WIZ hashing + * disable bit, which we don't touch here, but it's good + * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). + */ + wa_add(wal, GEN7_GT_MODE, 0, + _MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4), + GEN6_WIZ_HASHING_16x4); +} + +static void +vlv_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal) +{ + /* WaDisableEarlyCull:vlv */ + wa_masked_en(wal, _3D_CHICKEN3, _3D_CHICKEN_SF_DISABLE_OBJEND_CULL); + + /* WaPsdDispatchEnable:vlv */ + /* WaDisablePSDDualDispatchEnable:vlv */ + wa_masked_en(wal, + GEN7_HALF_SLICE_CHICKEN1, + GEN7_MAX_PS_THREAD_DEP | + GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE); + + /* WaDisable_RenderCache_OperationalFlush:vlv */ + wa_masked_dis(wal, CACHE_MODE_0_GEN7, RC_OP_FLUSH_ENABLE); + + /* WaForceL3Serialization:vlv */ + wa_write_clr(wal, GEN7_L3SQCREG4, L3SQ_URB_READ_CAM_MATCH_DISABLE); + + /* + * WaVSThreadDispatchOverride:ivb,vlv + * + * This actually overrides the dispatch + * mode for all thread types. + */ + wa_write_masked_or(wal, + GEN7_FF_THREAD_MODE, + GEN7_FF_SCHED_MASK, + GEN7_FF_TS_SCHED_HW | + GEN7_FF_VS_SCHED_HW | + GEN7_FF_DS_SCHED_HW); + + /* + * BSpec says this must be set, even though + * WaDisable4x2SubspanOptimization isn't listed for VLV. + */ + wa_masked_en(wal, CACHE_MODE_1, PIXEL_SUBSPAN_COLLECT_OPT_DISABLE); + + /* + * BSpec recommends 8x4 when MSAA is used, + * however in practice 16x4 seems fastest. + * + * Note that PS/WM thread counts depend on the WIZ hashing + * disable bit, which we don't touch here, but it's good + * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). + */ + wa_add(wal, GEN7_GT_MODE, 0, + _MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4), + GEN6_WIZ_HASHING_16x4); + + /* + * WaIncreaseL3CreditsForVLVB0:vlv + * This is the hardware default actually. + */ + wa_write(wal, GEN7_L3SQCREG1, VLV_B0_WA_L3SQCREG1_VALUE); +} + +static void +hsw_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal) +{ + /* L3 caching of data atomics doesn't work -- disable it. */ + wa_write(wal, HSW_SCRATCH1, HSW_SCRATCH1_L3_DATA_ATOMICS_DISABLE); + + wa_add(wal, + HSW_ROW_CHICKEN3, 0, + _MASKED_BIT_ENABLE(HSW_ROW_CHICKEN3_L3_GLOBAL_ATOMICS_DISABLE), + 0 /* XXX does this reg exist? */); + + /* WaVSRefCountFullforceMissDisable:hsw */ + wa_write_clr(wal, GEN7_FF_THREAD_MODE, GEN7_FF_VS_REF_CNT_FFME); + + wa_masked_dis(wal, + CACHE_MODE_0_GEN7, + /* WaDisable_RenderCache_OperationalFlush:hsw */ + RC_OP_FLUSH_ENABLE | + /* enable HiZ Raw Stall Optimization */ + HIZ_RAW_STALL_OPT_DISABLE); + + /* WaDisable4x2SubspanOptimization:hsw */ + wa_masked_en(wal, CACHE_MODE_1, PIXEL_SUBSPAN_COLLECT_OPT_DISABLE); + + /* + * BSpec recommends 8x4 when MSAA is used, + * however in practice 16x4 seems fastest. + * + * Note that PS/WM thread counts depend on the WIZ hashing + * disable bit, which we don't touch here, but it's good + * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). + */ + wa_add(wal, GEN7_GT_MODE, 0, + _MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4), + GEN6_WIZ_HASHING_16x4); + + /* WaSampleCChickenBitEnable:hsw */ + wa_masked_en(wal, HALF_SLICE_CHICKEN3, HSW_SAMPLE_C_PERFORMANCE); +} + +static void gen9_gt_workarounds_init(struct drm_i915_private *i915, struct i915_wa_list *wal) { /* WaDisableKillLogic:bxt,skl,kbl */ @@ -963,6 +1190,20 @@ gt_init_workarounds(struct drm_i915_private *i915, struct i915_wa_list *wal) bxt_gt_workarounds_init(i915, wal); else if (IS_SKYLAKE(i915)) skl_gt_workarounds_init(i915, wal); + else if (IS_HASWELL(i915)) + hsw_gt_workarounds_init(i915, wal); + else if (IS_VALLEYVIEW(i915)) + vlv_gt_workarounds_init(i915, wal); + else if (IS_IVYBRIDGE(i915)) + ivb_gt_workarounds_init(i915, wal); + else if (IS_GEN(i915, 6)) + snb_gt_workarounds_init(i915, wal); + else if (IS_GEN(i915, 5)) + ilk_gt_workarounds_init(i915, wal); + else if (IS_G4X(i915)) + g4x_gt_workarounds_init(i915, wal); + else if (IS_GEN(i915, 4)) + gen4_gt_workarounds_init(i915, wal); else if (INTEL_GEN(i915) <= 8) return; else diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c index 2b2efff6e19d..4aa4cc917d8b 100644 --- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c +++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c @@ -310,22 +310,20 @@ static bool wait_until_running(struct hang *h, struct i915_request *rq) 1000)); } -static void engine_heartbeat_disable(struct intel_engine_cs *engine, - unsigned long *saved) +static void engine_heartbeat_disable(struct intel_engine_cs *engine) { - *saved = engine->props.heartbeat_interval_ms; engine->props.heartbeat_interval_ms = 0; intel_engine_pm_get(engine); intel_engine_park_heartbeat(engine); } -static void engine_heartbeat_enable(struct intel_engine_cs *engine, - unsigned long saved) +static void engine_heartbeat_enable(struct intel_engine_cs *engine) { intel_engine_pm_put(engine); - engine->props.heartbeat_interval_ms = saved; + engine->props.heartbeat_interval_ms = + engine->defaults.heartbeat_interval_ms; } static int igt_hang_sanitycheck(void *arg) @@ -473,7 +471,6 @@ static int igt_reset_nop_engine(void *arg) for_each_engine(engine, gt, id) { unsigned int reset_count, reset_engine_count, count; struct intel_context *ce; - unsigned long heartbeat; IGT_TIMEOUT(end_time); int err; @@ -485,7 +482,7 @@ static int igt_reset_nop_engine(void *arg) reset_engine_count = i915_reset_engine_count(global, engine); count = 0; - engine_heartbeat_disable(engine, &heartbeat); + engine_heartbeat_disable(engine); set_bit(I915_RESET_ENGINE + id, >->reset.flags); do { int i; @@ -529,7 +526,7 @@ static int igt_reset_nop_engine(void *arg) } } while (time_before(jiffies, end_time)); clear_bit(I915_RESET_ENGINE + id, >->reset.flags); - engine_heartbeat_enable(engine, heartbeat); + engine_heartbeat_enable(engine); pr_info("%s(%s): %d resets\n", __func__, engine->name, count); @@ -564,7 +561,6 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active) for_each_engine(engine, gt, id) { unsigned int reset_count, reset_engine_count; - unsigned long heartbeat; IGT_TIMEOUT(end_time); if (active && !intel_engine_can_store_dword(engine)) @@ -580,7 +576,7 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active) reset_count = i915_reset_count(global); reset_engine_count = i915_reset_engine_count(global, engine); - engine_heartbeat_disable(engine, &heartbeat); + engine_heartbeat_disable(engine); set_bit(I915_RESET_ENGINE + id, >->reset.flags); do { if (active) { @@ -632,7 +628,7 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active) } } while (time_before(jiffies, end_time)); clear_bit(I915_RESET_ENGINE + id, >->reset.flags); - engine_heartbeat_enable(engine, heartbeat); + engine_heartbeat_enable(engine); if (err) break; @@ -789,7 +785,6 @@ static int __igt_reset_engines(struct intel_gt *gt, struct active_engine threads[I915_NUM_ENGINES] = {}; unsigned long device = i915_reset_count(global); unsigned long count = 0, reported; - unsigned long heartbeat; IGT_TIMEOUT(end_time); if (flags & TEST_ACTIVE && @@ -832,7 +827,7 @@ static int __igt_reset_engines(struct intel_gt *gt, yield(); /* start all threads before we begin */ - engine_heartbeat_disable(engine, &heartbeat); + engine_heartbeat_disable(engine); set_bit(I915_RESET_ENGINE + id, >->reset.flags); do { struct i915_request *rq = NULL; @@ -906,7 +901,7 @@ static int __igt_reset_engines(struct intel_gt *gt, } } while (time_before(jiffies, end_time)); clear_bit(I915_RESET_ENGINE + id, >->reset.flags); - engine_heartbeat_enable(engine, heartbeat); + engine_heartbeat_enable(engine); pr_info("i915_reset_engine(%s:%s): %lu resets\n", engine->name, test_name, count); diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c index 824f99c4cc7c..924bc01ef526 100644 --- a/drivers/gpu/drm/i915/gt/selftest_lrc.c +++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c @@ -51,22 +51,20 @@ static struct i915_vma *create_scratch(struct intel_gt *gt) return vma; } -static void engine_heartbeat_disable(struct intel_engine_cs *engine, - unsigned long *saved) +static void engine_heartbeat_disable(struct intel_engine_cs *engine) { - *saved = engine->props.heartbeat_interval_ms; engine->props.heartbeat_interval_ms = 0; intel_engine_pm_get(engine); intel_engine_park_heartbeat(engine); } -static void engine_heartbeat_enable(struct intel_engine_cs *engine, - unsigned long saved) +static void engine_heartbeat_enable(struct intel_engine_cs *engine) { intel_engine_pm_put(engine); - engine->props.heartbeat_interval_ms = saved; + engine->props.heartbeat_interval_ms = + engine->defaults.heartbeat_interval_ms; } static bool is_active(struct i915_request *rq) @@ -224,7 +222,6 @@ static int live_unlite_restore(struct intel_gt *gt, int prio) struct intel_context *ce[2] = {}; struct i915_request *rq[2]; struct igt_live_test t; - unsigned long saved; int n; if (prio && !intel_engine_has_preemption(engine)) @@ -237,7 +234,7 @@ static int live_unlite_restore(struct intel_gt *gt, int prio) err = -EIO; break; } - engine_heartbeat_disable(engine, &saved); + engine_heartbeat_disable(engine); for (n = 0; n < ARRAY_SIZE(ce); n++) { struct intel_context *tmp; @@ -345,7 +342,7 @@ err_ce: intel_context_put(ce[n]); } - engine_heartbeat_enable(engine, saved); + engine_heartbeat_enable(engine); if (igt_live_test_end(&t)) err = -EIO; if (err) @@ -466,7 +463,6 @@ static int live_hold_reset(void *arg) for_each_engine(engine, gt, id) { struct intel_context *ce; - unsigned long heartbeat; struct i915_request *rq; ce = intel_context_create(engine); @@ -475,7 +471,7 @@ static int live_hold_reset(void *arg) break; } - engine_heartbeat_disable(engine, &heartbeat); + engine_heartbeat_disable(engine); rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK); if (IS_ERR(rq)) { @@ -535,7 +531,7 @@ static int live_hold_reset(void *arg) i915_request_put(rq); out: - engine_heartbeat_enable(engine, heartbeat); + engine_heartbeat_enable(engine); intel_context_put(ce); if (err) break; @@ -580,10 +576,9 @@ static int live_error_interrupt(void *arg) for_each_engine(engine, gt, id) { const struct error_phase *p; - unsigned long heartbeat; int err = 0; - engine_heartbeat_disable(engine, &heartbeat); + engine_heartbeat_disable(engine); for (p = phases; p->error[0] != GOOD; p++) { struct i915_request *client[ARRAY_SIZE(phases->error)]; @@ -682,7 +677,7 @@ out: } } - engine_heartbeat_enable(engine, heartbeat); + engine_heartbeat_enable(engine); if (err) { intel_gt_set_wedged(gt); return err; @@ -828,7 +823,7 @@ slice_semaphore_queue(struct intel_engine_cs *outer, } } - err = release_queue(outer, vma, n, INT_MAX); + err = release_queue(outer, vma, n, I915_PRIORITY_BARRIER); if (err) goto out; @@ -895,16 +890,14 @@ static int live_timeslice_preempt(void *arg) enum intel_engine_id id; for_each_engine(engine, gt, id) { - unsigned long saved; - if (!intel_engine_has_preemption(engine)) continue; memset(vaddr, 0, PAGE_SIZE); - engine_heartbeat_disable(engine, &saved); + engine_heartbeat_disable(engine); err = slice_semaphore_queue(engine, vma, count); - engine_heartbeat_enable(engine, saved); + engine_heartbeat_enable(engine); if (err) goto err_pin; @@ -1009,7 +1002,6 @@ static int live_timeslice_rewind(void *arg) enum { X = 1, Z, Y }; struct i915_request *rq[3] = {}; struct intel_context *ce; - unsigned long heartbeat; unsigned long timeslice; int i, err = 0; u32 *slot; @@ -1028,7 +1020,7 @@ static int live_timeslice_rewind(void *arg) * Expect execution/evaluation order XZY */ - engine_heartbeat_disable(engine, &heartbeat); + engine_heartbeat_disable(engine); timeslice = xchg(&engine->props.timeslice_duration_ms, 1); slot = memset32(engine->status_page.addr + 1000, 0, 4); @@ -1122,7 +1114,7 @@ err: wmb(); engine->props.timeslice_duration_ms = timeslice; - engine_heartbeat_enable(engine, heartbeat); + engine_heartbeat_enable(engine); for (i = 0; i < 3; i++) i915_request_put(rq[i]); if (igt_flush_test(gt->i915)) @@ -1202,12 +1194,11 @@ static int live_timeslice_queue(void *arg) .priority = I915_USER_PRIORITY(I915_PRIORITY_MAX), }; struct i915_request *rq, *nop; - unsigned long saved; if (!intel_engine_has_preemption(engine)) continue; - engine_heartbeat_disable(engine, &saved); + engine_heartbeat_disable(engine); memset(vaddr, 0, PAGE_SIZE); /* ELSP[0]: semaphore wait */ @@ -1284,7 +1275,7 @@ static int live_timeslice_queue(void *arg) err_rq: i915_request_put(rq); err_heartbeat: - engine_heartbeat_enable(engine, saved); + engine_heartbeat_enable(engine); if (err) break; } @@ -1298,6 +1289,121 @@ err_obj: return err; } +static int live_timeslice_nopreempt(void *arg) +{ + struct intel_gt *gt = arg; + struct intel_engine_cs *engine; + enum intel_engine_id id; + struct igt_spinner spin; + int err = 0; + + /* + * We should not timeslice into a request that is marked with + * I915_REQUEST_NOPREEMPT. + */ + if (!IS_ACTIVE(CONFIG_DRM_I915_TIMESLICE_DURATION)) + return 0; + + if (igt_spinner_init(&spin, gt)) + return -ENOMEM; + + for_each_engine(engine, gt, id) { + struct intel_context *ce; + struct i915_request *rq; + unsigned long timeslice; + + if (!intel_engine_has_preemption(engine)) + continue; + + ce = intel_context_create(engine); + if (IS_ERR(ce)) { + err = PTR_ERR(ce); + break; + } + + engine_heartbeat_disable(engine); + timeslice = xchg(&engine->props.timeslice_duration_ms, 1); + + /* Create an unpreemptible spinner */ + + rq = igt_spinner_create_request(&spin, ce, MI_ARB_CHECK); + intel_context_put(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto out_heartbeat; + } + + i915_request_get(rq); + i915_request_add(rq); + + if (!igt_wait_for_spinner(&spin, rq)) { + i915_request_put(rq); + err = -ETIME; + goto out_spin; + } + + set_bit(I915_FENCE_FLAG_NOPREEMPT, &rq->fence.flags); + i915_request_put(rq); + + /* Followed by a maximum priority barrier (heartbeat) */ + + ce = intel_context_create(engine); + if (IS_ERR(ce)) { + err = PTR_ERR(rq); + goto out_spin; + } + + rq = intel_context_create_request(ce); + intel_context_put(ce); + if (IS_ERR(rq)) { + err = PTR_ERR(rq); + goto out_spin; + } + + rq->sched.attr.priority = I915_PRIORITY_BARRIER; + i915_request_get(rq); + i915_request_add(rq); + + /* + * Wait until the barrier is in ELSP, and we know timeslicing + * will have been activated. + */ + if (wait_for_submit(engine, rq, HZ / 2)) { + i915_request_put(rq); + err = -ETIME; + goto out_spin; + } + + /* + * Since the ELSP[0] request is unpreemptible, it should not + * allow the maximum priority barrier through. Wait long + * enough to see if it is timesliced in by mistake. + */ + if (i915_request_wait(rq, 0, timeslice_threshold(engine)) >= 0) { + pr_err("%s: I915_PRIORITY_BARRIER request completed, bypassing no-preempt request\n", + engine->name); + err = -EINVAL; + } + i915_request_put(rq); + +out_spin: + igt_spinner_end(&spin); +out_heartbeat: + xchg(&engine->props.timeslice_duration_ms, timeslice); + engine_heartbeat_enable(engine); + if (err) + break; + + if (igt_flush_test(gt->i915)) { + err = -EIO; + break; + } + } + + igt_spinner_fini(&spin); + return err; +} + static int live_busywait_preempt(void *arg) { struct intel_gt *gt = arg; @@ -4153,7 +4259,6 @@ static int reset_virtual_engine(struct intel_gt *gt, { struct intel_engine_cs *engine; struct intel_context *ve; - unsigned long *heartbeat; struct igt_spinner spin; struct i915_request *rq; unsigned int n; @@ -4165,15 +4270,9 @@ static int reset_virtual_engine(struct intel_gt *gt, * descendents are not executed while the capture is in progress. */ - heartbeat = kmalloc_array(nsibling, sizeof(*heartbeat), GFP_KERNEL); - if (!heartbeat) + if (igt_spinner_init(&spin, gt)) return -ENOMEM; - if (igt_spinner_init(&spin, gt)) { - err = -ENOMEM; - goto out_free; - } - ve = intel_execlists_create_virtual(siblings, nsibling); if (IS_ERR(ve)) { err = PTR_ERR(ve); @@ -4181,7 +4280,7 @@ static int reset_virtual_engine(struct intel_gt *gt, } for (n = 0; n < nsibling; n++) - engine_heartbeat_disable(siblings[n], &heartbeat[n]); + engine_heartbeat_disable(siblings[n]); rq = igt_spinner_create_request(&spin, ve, MI_ARB_CHECK); if (IS_ERR(rq)) { @@ -4252,13 +4351,11 @@ out_rq: i915_request_put(rq); out_heartbeat: for (n = 0; n < nsibling; n++) - engine_heartbeat_enable(siblings[n], heartbeat[n]); + engine_heartbeat_enable(siblings[n]); intel_context_put(ve); out_spin: igt_spinner_fini(&spin); -out_free: - kfree(heartbeat); return err; } @@ -4314,6 +4411,7 @@ int intel_execlists_live_selftests(struct drm_i915_private *i915) SUBTEST(live_timeslice_preempt), SUBTEST(live_timeslice_rewind), SUBTEST(live_timeslice_queue), + SUBTEST(live_timeslice_nopreempt), SUBTEST(live_busywait_preempt), SUBTEST(live_preempt), SUBTEST(live_late_preempt), @@ -4932,9 +5030,7 @@ static int live_lrc_gpr(void *arg) return PTR_ERR(scratch); for_each_engine(engine, gt, id) { - unsigned long heartbeat; - - engine_heartbeat_disable(engine, &heartbeat); + engine_heartbeat_disable(engine); err = __live_lrc_gpr(engine, scratch, false); if (err) @@ -4945,7 +5041,7 @@ static int live_lrc_gpr(void *arg) goto err; err: - engine_heartbeat_enable(engine, heartbeat); + engine_heartbeat_enable(engine); if (igt_flush_test(gt->i915)) err = -EIO; if (err) @@ -5092,10 +5188,9 @@ static int live_lrc_timestamp(void *arg) */ for_each_engine(data.engine, gt, id) { - unsigned long heartbeat; int i, err = 0; - engine_heartbeat_disable(data.engine, &heartbeat); + engine_heartbeat_disable(data.engine); for (i = 0; i < ARRAY_SIZE(data.ce); i++) { struct intel_context *tmp; @@ -5128,7 +5223,7 @@ static int live_lrc_timestamp(void *arg) } err: - engine_heartbeat_enable(data.engine, heartbeat); + engine_heartbeat_enable(data.engine); for (i = 0; i < ARRAY_SIZE(data.ce); i++) { if (!data.ce[i]) break; diff --git a/drivers/gpu/drm/i915/gt/selftest_mocs.c b/drivers/gpu/drm/i915/gt/selftest_mocs.c index 8831ffee2061..63f87d8608c3 100644 --- a/drivers/gpu/drm/i915/gt/selftest_mocs.c +++ b/drivers/gpu/drm/i915/gt/selftest_mocs.c @@ -18,6 +18,20 @@ struct live_mocs { void *vaddr; }; +static struct intel_context *mocs_context_create(struct intel_engine_cs *engine) +{ + struct intel_context *ce; + + ce = intel_context_create(engine); + if (IS_ERR(ce)) + return ce; + + /* We build large requests to read the registers from the ring */ + ce->ring = __intel_context_ring_size(SZ_16K); + + return ce; +} + static int request_add_sync(struct i915_request *rq, int err) { i915_request_get(rq); @@ -301,7 +315,7 @@ static int live_mocs_clean(void *arg) for_each_engine(engine, gt, id) { struct intel_context *ce; - ce = intel_context_create(engine); + ce = mocs_context_create(engine); if (IS_ERR(ce)) { err = PTR_ERR(ce); break; @@ -395,7 +409,7 @@ static int live_mocs_reset(void *arg) for_each_engine(engine, gt, id) { struct intel_context *ce; - ce = intel_context_create(engine); + ce = mocs_context_create(engine); if (IS_ERR(ce)) { err = PTR_ERR(ce); break; diff --git a/drivers/gpu/drm/i915/gt/selftest_ring.c b/drivers/gpu/drm/i915/gt/selftest_ring.c new file mode 100644 index 000000000000..2a8c534dc125 --- /dev/null +++ b/drivers/gpu/drm/i915/gt/selftest_ring.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright © 2020 Intel Corporation + */ + +static struct intel_ring *mock_ring(unsigned long sz) +{ + struct intel_ring *ring; + + ring = kzalloc(sizeof(*ring) + sz, GFP_KERNEL); + if (!ring) + return NULL; + + kref_init(&ring->ref); + ring->size = sz; + ring->wrap = BITS_PER_TYPE(ring->size) - ilog2(sz); + ring->effective_size = sz; + ring->vaddr = (void *)(ring + 1); + atomic_set(&ring->pin_count, 1); + + intel_ring_update_space(ring); + + return ring; +} + +static void mock_ring_free(struct intel_ring *ring) +{ + kfree(ring); +} + +static int check_ring_direction(struct intel_ring *ring, + u32 next, u32 prev, + int expected) +{ + int result; + + result = intel_ring_direction(ring, next, prev); + if (result < 0) + result = -1; + else if (result > 0) + result = 1; + + if (result != expected) { + pr_err("intel_ring_direction(%u, %u):%d != %d\n", + next, prev, result, expected); + return -EINVAL; + } + + return 0; +} + +static int check_ring_step(struct intel_ring *ring, u32 x, u32 step) +{ + u32 prev = x, next = intel_ring_wrap(ring, x + step); + int err = 0; + + err |= check_ring_direction(ring, next, next, 0); + err |= check_ring_direction(ring, prev, prev, 0); + err |= check_ring_direction(ring, next, prev, 1); + err |= check_ring_direction(ring, prev, next, -1); + + return err; +} + +static int check_ring_offset(struct intel_ring *ring, u32 x, u32 step) +{ + int err = 0; + + err |= check_ring_step(ring, x, step); + err |= check_ring_step(ring, intel_ring_wrap(ring, x + 1), step); + err |= check_ring_step(ring, intel_ring_wrap(ring, x - 1), step); + + return err; +} + +static int igt_ring_direction(void *dummy) +{ + struct intel_ring *ring; + unsigned int half = 2048; + int step, err = 0; + + ring = mock_ring(2 * half); + if (!ring) + return -ENOMEM; + + GEM_BUG_ON(ring->size != 2 * half); + + /* Precision of wrap detection is limited to ring->size / 2 */ + for (step = 1; step < half; step <<= 1) { + err |= check_ring_offset(ring, 0, step); + err |= check_ring_offset(ring, half, step); + } + err |= check_ring_step(ring, 0, half - 64); + + /* And check unwrapped handling for good measure */ + err |= check_ring_offset(ring, 0, 2 * half + 64); + err |= check_ring_offset(ring, 3 * half, 1); + + mock_ring_free(ring); + return err; +} + +int intel_ring_mock_selftests(void) +{ + static const struct i915_subtest tests[] = { + SUBTEST(igt_ring_direction), + }; + + return i915_subtests(tests, NULL); +} diff --git a/drivers/gpu/drm/i915/gt/selftest_rps.c b/drivers/gpu/drm/i915/gt/selftest_rps.c index 6275d69aa9cc..5049c3dd08a6 100644 --- a/drivers/gpu/drm/i915/gt/selftest_rps.c +++ b/drivers/gpu/drm/i915/gt/selftest_rps.c @@ -20,24 +20,20 @@ /* Try to isolate the impact of cstates from determing frequency response */ #define CPU_LATENCY 0 /* -1 to disable pm_qos, 0 to disable cstates */ -static unsigned long engine_heartbeat_disable(struct intel_engine_cs *engine) +static void engine_heartbeat_disable(struct intel_engine_cs *engine) { - unsigned long old; - - old = fetch_and_zero(&engine->props.heartbeat_interval_ms); + engine->props.heartbeat_interval_ms = 0; intel_engine_pm_get(engine); intel_engine_park_heartbeat(engine); - - return old; } -static void engine_heartbeat_enable(struct intel_engine_cs *engine, - unsigned long saved) +static void engine_heartbeat_enable(struct intel_engine_cs *engine) { intel_engine_pm_put(engine); - engine->props.heartbeat_interval_ms = saved; + engine->props.heartbeat_interval_ms = + engine->defaults.heartbeat_interval_ms; } static void dummy_rps_work(struct work_struct *wrk) @@ -246,7 +242,6 @@ int live_rps_clock_interval(void *arg) intel_gt_check_clock_frequency(gt); for_each_engine(engine, gt, id) { - unsigned long saved_heartbeat; struct i915_request *rq; u32 cycles; u64 dt; @@ -254,13 +249,13 @@ int live_rps_clock_interval(void *arg) if (!intel_engine_can_store_dword(engine)) continue; - saved_heartbeat = engine_heartbeat_disable(engine); + engine_heartbeat_disable(engine); rq = igt_spinner_create_request(&spin, engine->kernel_context, MI_NOOP); if (IS_ERR(rq)) { - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); err = PTR_ERR(rq); break; } @@ -271,7 +266,7 @@ int live_rps_clock_interval(void *arg) pr_err("%s: RPS spinner did not start\n", engine->name); igt_spinner_end(&spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); intel_gt_set_wedged(engine->gt); err = -EIO; break; @@ -327,7 +322,7 @@ int live_rps_clock_interval(void *arg) intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL); igt_spinner_end(&spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); if (err == 0) { u64 time = intel_gt_pm_interval_to_ns(gt, cycles); @@ -405,7 +400,6 @@ int live_rps_control(void *arg) intel_gt_pm_get(gt); for_each_engine(engine, gt, id) { - unsigned long saved_heartbeat; struct i915_request *rq; ktime_t min_dt, max_dt; int f, limit; @@ -414,7 +408,7 @@ int live_rps_control(void *arg) if (!intel_engine_can_store_dword(engine)) continue; - saved_heartbeat = engine_heartbeat_disable(engine); + engine_heartbeat_disable(engine); rq = igt_spinner_create_request(&spin, engine->kernel_context, @@ -430,7 +424,7 @@ int live_rps_control(void *arg) pr_err("%s: RPS spinner did not start\n", engine->name); igt_spinner_end(&spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); intel_gt_set_wedged(engine->gt); err = -EIO; break; @@ -440,7 +434,7 @@ int live_rps_control(void *arg) pr_err("%s: could not set minimum frequency [%x], only %x!\n", engine->name, rps->min_freq, read_cagf(rps)); igt_spinner_end(&spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); show_pstate_limits(rps); err = -EINVAL; break; @@ -457,7 +451,7 @@ int live_rps_control(void *arg) pr_err("%s: could not restore minimum frequency [%x], only %x!\n", engine->name, rps->min_freq, read_cagf(rps)); igt_spinner_end(&spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); show_pstate_limits(rps); err = -EINVAL; break; @@ -472,7 +466,7 @@ int live_rps_control(void *arg) min_dt = ktime_sub(ktime_get(), min_dt); igt_spinner_end(&spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); pr_info("%s: range:[%x:%uMHz, %x:%uMHz] limit:[%x:%uMHz], %x:%x response %lluns:%lluns\n", engine->name, @@ -635,7 +629,6 @@ int live_rps_frequency_cs(void *arg) rps->work.func = dummy_rps_work; for_each_engine(engine, gt, id) { - unsigned long saved_heartbeat; struct i915_request *rq; struct i915_vma *vma; u32 *cancel, *cntr; @@ -644,14 +637,14 @@ int live_rps_frequency_cs(void *arg) int freq; } min, max; - saved_heartbeat = engine_heartbeat_disable(engine); + engine_heartbeat_disable(engine); vma = create_spin_counter(engine, engine->kernel_context->vm, false, &cancel, &cntr); if (IS_ERR(vma)) { err = PTR_ERR(vma); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); break; } @@ -732,7 +725,7 @@ err_vma: i915_vma_unpin(vma); i915_vma_put(vma); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); if (igt_flush_test(gt->i915)) err = -EIO; if (err) @@ -778,7 +771,6 @@ int live_rps_frequency_srm(void *arg) rps->work.func = dummy_rps_work; for_each_engine(engine, gt, id) { - unsigned long saved_heartbeat; struct i915_request *rq; struct i915_vma *vma; u32 *cancel, *cntr; @@ -787,14 +779,14 @@ int live_rps_frequency_srm(void *arg) int freq; } min, max; - saved_heartbeat = engine_heartbeat_disable(engine); + engine_heartbeat_disable(engine); vma = create_spin_counter(engine, engine->kernel_context->vm, true, &cancel, &cntr); if (IS_ERR(vma)) { err = PTR_ERR(vma); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); break; } @@ -874,7 +866,7 @@ err_vma: i915_vma_unpin(vma); i915_vma_put(vma); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); if (igt_flush_test(gt->i915)) err = -EIO; if (err) @@ -1066,16 +1058,14 @@ int live_rps_interrupt(void *arg) for_each_engine(engine, gt, id) { /* Keep the engine busy with a spinner; expect an UP! */ if (pm_events & GEN6_PM_RP_UP_THRESHOLD) { - unsigned long saved_heartbeat; - intel_gt_pm_wait_for_idle(engine->gt); GEM_BUG_ON(intel_rps_is_active(rps)); - saved_heartbeat = engine_heartbeat_disable(engine); + engine_heartbeat_disable(engine); err = __rps_up_interrupt(rps, engine, &spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); if (err) goto out; @@ -1084,15 +1074,13 @@ int live_rps_interrupt(void *arg) /* Keep the engine awake but idle and check for DOWN */ if (pm_events & GEN6_PM_RP_DOWN_THRESHOLD) { - unsigned long saved_heartbeat; - - saved_heartbeat = engine_heartbeat_disable(engine); + engine_heartbeat_disable(engine); intel_rc6_disable(>->rc6); err = __rps_down_interrupt(rps, engine); intel_rc6_enable(>->rc6); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); if (err) goto out; } @@ -1168,7 +1156,6 @@ int live_rps_power(void *arg) rps->work.func = dummy_rps_work; for_each_engine(engine, gt, id) { - unsigned long saved_heartbeat; struct i915_request *rq; struct { u64 power; @@ -1178,13 +1165,13 @@ int live_rps_power(void *arg) if (!intel_engine_can_store_dword(engine)) continue; - saved_heartbeat = engine_heartbeat_disable(engine); + engine_heartbeat_disable(engine); rq = igt_spinner_create_request(&spin, engine->kernel_context, MI_NOOP); if (IS_ERR(rq)) { - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); err = PTR_ERR(rq); break; } @@ -1195,7 +1182,7 @@ int live_rps_power(void *arg) pr_err("%s: RPS spinner did not start\n", engine->name); igt_spinner_end(&spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); intel_gt_set_wedged(engine->gt); err = -EIO; break; @@ -1208,7 +1195,7 @@ int live_rps_power(void *arg) min.power = measure_power_at(rps, &min.freq); igt_spinner_end(&spin); - engine_heartbeat_enable(engine, saved_heartbeat); + engine_heartbeat_enable(engine); pr_info("%s: min:%llumW @ %uMHz, max:%llumW @ %uMHz\n", engine->name, diff --git a/drivers/gpu/drm/i915/gt/selftest_timeline.c b/drivers/gpu/drm/i915/gt/selftest_timeline.c index c2578a0f2f14..ef1c35073dc0 100644 --- a/drivers/gpu/drm/i915/gt/selftest_timeline.c +++ b/drivers/gpu/drm/i915/gt/selftest_timeline.c @@ -751,22 +751,20 @@ out_free: return err; } -static void engine_heartbeat_disable(struct intel_engine_cs *engine, - unsigned long *saved) +static void engine_heartbeat_disable(struct intel_engine_cs *engine) { - *saved = engine->props.heartbeat_interval_ms; engine->props.heartbeat_interval_ms = 0; intel_engine_pm_get(engine); intel_engine_park_heartbeat(engine); } -static void engine_heartbeat_enable(struct intel_engine_cs *engine, - unsigned long saved) +static void engine_heartbeat_enable(struct intel_engine_cs *engine) { intel_engine_pm_put(engine); - engine->props.heartbeat_interval_ms = saved; + engine->props.heartbeat_interval_ms = + engine->defaults.heartbeat_interval_ms; } static int live_hwsp_rollover_kernel(void *arg) @@ -785,10 +783,9 @@ static int live_hwsp_rollover_kernel(void *arg) struct intel_context *ce = engine->kernel_context; struct intel_timeline *tl = ce->timeline; struct i915_request *rq[3] = {}; - unsigned long heartbeat; int i; - engine_heartbeat_disable(engine, &heartbeat); + engine_heartbeat_disable(engine); if (intel_gt_wait_for_idle(gt, HZ / 2)) { err = -EIO; goto out; @@ -839,7 +836,7 @@ static int live_hwsp_rollover_kernel(void *arg) out: for (i = 0; i < ARRAY_SIZE(rq); i++) i915_request_put(rq[i]); - engine_heartbeat_enable(engine, heartbeat); + engine_heartbeat_enable(engine); if (err) break; } diff --git a/drivers/gpu/drm/i915/gt/selftest_workarounds.c b/drivers/gpu/drm/i915/gt/selftest_workarounds.c index 5ed323254ee1..32785463ec9e 100644 --- a/drivers/gpu/drm/i915/gt/selftest_workarounds.c +++ b/drivers/gpu/drm/i915/gt/selftest_workarounds.c @@ -623,6 +623,8 @@ err_request: err = -EINVAL; goto out_unpin; } + } else { + rsvd = 0; } expect = results[0]; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 4dc601dffc08..284cf078135a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -3125,6 +3125,7 @@ static void gen11_hpd_irq_setup(struct drm_i915_private *dev_priv) val = I915_READ(GEN11_DE_HPD_IMR); val &= ~hotplug_irqs; + val |= ~enabled_irqs & hotplug_irqs; I915_WRITE(GEN11_DE_HPD_IMR, val); POSTING_READ(GEN11_DE_HPD_IMR); diff --git a/drivers/gpu/drm/i915/i915_pmu.c b/drivers/gpu/drm/i915/i915_pmu.c index e991a707bdb7..962ded9ce73f 100644 --- a/drivers/gpu/drm/i915/i915_pmu.c +++ b/drivers/gpu/drm/i915/i915_pmu.c @@ -269,12 +269,48 @@ static bool exclusive_mmio_access(const struct drm_i915_private *i915) return IS_GEN(i915, 7); } +static void engine_sample(struct intel_engine_cs *engine, unsigned int period_ns) +{ + struct intel_engine_pmu *pmu = &engine->pmu; + bool busy; + u32 val; + + val = ENGINE_READ_FW(engine, RING_CTL); + if (val == 0) /* powerwell off => engine idle */ + return; + + if (val & RING_WAIT) + add_sample(&pmu->sample[I915_SAMPLE_WAIT], period_ns); + if (val & RING_WAIT_SEMAPHORE) + add_sample(&pmu->sample[I915_SAMPLE_SEMA], period_ns); + + /* No need to sample when busy stats are supported. */ + if (intel_engine_supports_stats(engine)) + return; + + /* + * While waiting on a semaphore or event, MI_MODE reports the + * ring as idle. However, previously using the seqno, and with + * execlists sampling, we account for the ring waiting as the + * engine being busy. Therefore, we record the sample as being + * busy if either waiting or !idle. + */ + busy = val & (RING_WAIT_SEMAPHORE | RING_WAIT); + if (!busy) { + val = ENGINE_READ_FW(engine, RING_MI_MODE); + busy = !(val & MODE_IDLE); + } + if (busy) + add_sample(&pmu->sample[I915_SAMPLE_BUSY], period_ns); +} + static void engines_sample(struct intel_gt *gt, unsigned int period_ns) { struct drm_i915_private *i915 = gt->i915; struct intel_engine_cs *engine; enum intel_engine_id id; + unsigned long flags; if ((i915->pmu.enable & ENGINE_SAMPLE_MASK) == 0) return; @@ -283,53 +319,17 @@ engines_sample(struct intel_gt *gt, unsigned int period_ns) return; for_each_engine(engine, gt, id) { - struct intel_engine_pmu *pmu = &engine->pmu; - spinlock_t *mmio_lock; - unsigned long flags; - bool busy; - u32 val; - if (!intel_engine_pm_get_if_awake(engine)) continue; - mmio_lock = NULL; - if (exclusive_mmio_access(i915)) - mmio_lock = &engine->uncore->lock; - - if (unlikely(mmio_lock)) - spin_lock_irqsave(mmio_lock, flags); - - val = ENGINE_READ_FW(engine, RING_CTL); - if (val == 0) /* powerwell off => engine idle */ - goto skip; - - if (val & RING_WAIT) - add_sample(&pmu->sample[I915_SAMPLE_WAIT], period_ns); - if (val & RING_WAIT_SEMAPHORE) - add_sample(&pmu->sample[I915_SAMPLE_SEMA], period_ns); - - /* No need to sample when busy stats are supported. */ - if (intel_engine_supports_stats(engine)) - goto skip; - - /* - * While waiting on a semaphore or event, MI_MODE reports the - * ring as idle. However, previously using the seqno, and with - * execlists sampling, we account for the ring waiting as the - * engine being busy. Therefore, we record the sample as being - * busy if either waiting or !idle. - */ - busy = val & (RING_WAIT_SEMAPHORE | RING_WAIT); - if (!busy) { - val = ENGINE_READ_FW(engine, RING_MI_MODE); - busy = !(val & MODE_IDLE); + if (exclusive_mmio_access(i915)) { + spin_lock_irqsave(&engine->uncore->lock, flags); + engine_sample(engine, period_ns); + spin_unlock_irqrestore(&engine->uncore->lock, flags); + } else { + engine_sample(engine, period_ns); } - if (busy) - add_sample(&pmu->sample[I915_SAMPLE_BUSY], period_ns); -skip: - if (unlikely(mmio_lock)) - spin_unlock_irqrestore(mmio_lock, flags); intel_engine_pm_put_async(engine); } } diff --git a/drivers/gpu/drm/i915/i915_priolist_types.h b/drivers/gpu/drm/i915/i915_priolist_types.h index 5003a71113cb..8aa7866ec6b6 100644 --- a/drivers/gpu/drm/i915/i915_priolist_types.h +++ b/drivers/gpu/drm/i915/i915_priolist_types.h @@ -42,7 +42,7 @@ enum { * active request. */ #define I915_PRIORITY_UNPREEMPTABLE INT_MAX -#define I915_PRIORITY_BARRIER INT_MAX +#define I915_PRIORITY_BARRIER (I915_PRIORITY_UNPREEMPTABLE - 1) struct i915_priolist { struct list_head requests[I915_PRIORITY_COUNT]; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 7717581350bd..06cd1d28a176 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -7896,7 +7896,7 @@ enum { /* GEN7 chicken */ #define GEN7_COMMON_SLICE_CHICKEN1 _MMIO(0x7010) - #define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC ((1 << 10) | (1 << 26)) + #define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC (1 << 10) #define GEN9_RHWO_OPTIMIZATION_DISABLE (1 << 14) #define COMMON_SLICE_CHICKEN2 _MMIO(0x7014) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 696491d71a1d..07f663cd2d1c 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -6830,16 +6830,6 @@ static void ilk_init_clock_gating(struct drm_i915_private *dev_priv) I915_WRITE(ILK_DISPLAY_CHICKEN2, I915_READ(ILK_DISPLAY_CHICKEN2) | ILK_ELPIN_409_SELECT); - I915_WRITE(_3D_CHICKEN2, - _3D_CHICKEN2_WM_READ_PIPELINED << 16 | - _3D_CHICKEN2_WM_READ_PIPELINED); - - /* WaDisableRenderCachePipelinedFlush:ilk */ - I915_WRITE(CACHE_MODE_0, - _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); - - /* WaDisable_RenderCache_OperationalFlush:ilk */ - I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); g4x_disable_trickle_feed(dev_priv); @@ -6902,27 +6892,6 @@ static void gen6_init_clock_gating(struct drm_i915_private *dev_priv) I915_READ(ILK_DISPLAY_CHICKEN2) | ILK_ELPIN_409_SELECT); - /* WaDisableHiZPlanesWhenMSAAEnabled:snb */ - I915_WRITE(_3D_CHICKEN, - _MASKED_BIT_ENABLE(_3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB)); - - /* WaDisable_RenderCache_OperationalFlush:snb */ - I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); - - /* - * BSpec recoomends 8x4 when MSAA is used, - * however in practice 16x4 seems fastest. - * - * Note that PS/WM thread counts depend on the WIZ hashing - * disable bit, which we don't touch here, but it's good - * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). - */ - I915_WRITE(GEN6_GT_MODE, - _MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4)); - - I915_WRITE(CACHE_MODE_0, - _MASKED_BIT_DISABLE(CM0_STC_EVICT_DISABLE_LRA_SNB)); - I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) | GEN6_BLBUNIT_CLOCK_GATE_DISABLE | @@ -6945,18 +6914,6 @@ static void gen6_init_clock_gating(struct drm_i915_private *dev_priv) GEN6_RCPBUNIT_CLOCK_GATE_DISABLE | GEN6_RCCUNIT_CLOCK_GATE_DISABLE); - /* WaStripsFansDisableFastClipPerformanceFix:snb */ - I915_WRITE(_3D_CHICKEN3, - _MASKED_BIT_ENABLE(_3D_CHICKEN3_SF_DISABLE_FASTCLIP_CULL)); - - /* - * Bspec says: - * "This bit must be set if 3DSTATE_CLIP clip mode is set to normal and - * 3DSTATE_SF number of SF output attributes is more than 16." - */ - I915_WRITE(_3D_CHICKEN3, - _MASKED_BIT_ENABLE(_3D_CHICKEN3_SF_DISABLE_PIPELINED_ATTR_FETCH)); - /* * According to the spec the following bits should be * set in order to enable memory self-refresh and fbc: @@ -6986,24 +6943,6 @@ static void gen6_init_clock_gating(struct drm_i915_private *dev_priv) gen6_check_mch_setup(dev_priv); } -static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv) -{ - u32 reg = I915_READ(GEN7_FF_THREAD_MODE); - - /* - * WaVSThreadDispatchOverride:ivb,vlv - * - * This actually overrides the dispatch - * mode for all thread types. - */ - reg &= ~GEN7_FF_SCHED_MASK; - reg |= GEN7_FF_TS_SCHED_HW; - reg |= GEN7_FF_VS_SCHED_HW; - reg |= GEN7_FF_DS_SCHED_HW; - - I915_WRITE(GEN7_FF_THREAD_MODE, reg); -} - static void lpt_init_clock_gating(struct drm_i915_private *dev_priv) { /* @@ -7230,45 +7169,10 @@ static void bdw_init_clock_gating(struct drm_i915_private *dev_priv) static void hsw_init_clock_gating(struct drm_i915_private *dev_priv) { - /* L3 caching of data atomics doesn't work -- disable it. */ - I915_WRITE(HSW_SCRATCH1, HSW_SCRATCH1_L3_DATA_ATOMICS_DISABLE); - I915_WRITE(HSW_ROW_CHICKEN3, - _MASKED_BIT_ENABLE(HSW_ROW_CHICKEN3_L3_GLOBAL_ATOMICS_DISABLE)); - /* This is required by WaCatErrorRejectionIssue:hsw */ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, - I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | - GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - - /* WaVSRefCountFullforceMissDisable:hsw */ - I915_WRITE(GEN7_FF_THREAD_MODE, - I915_READ(GEN7_FF_THREAD_MODE) & ~GEN7_FF_VS_REF_CNT_FFME); - - /* WaDisable_RenderCache_OperationalFlush:hsw */ - I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); - - /* enable HiZ Raw Stall Optimization */ - I915_WRITE(CACHE_MODE_0_GEN7, - _MASKED_BIT_DISABLE(HIZ_RAW_STALL_OPT_DISABLE)); - - /* WaDisable4x2SubspanOptimization:hsw */ - I915_WRITE(CACHE_MODE_1, - _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); - - /* - * BSpec recommends 8x4 when MSAA is used, - * however in practice 16x4 seems fastest. - * - * Note that PS/WM thread counts depend on the WIZ hashing - * disable bit, which we don't touch here, but it's good - * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). - */ - I915_WRITE(GEN7_GT_MODE, - _MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4)); - - /* WaSampleCChickenBitEnable:hsw */ - I915_WRITE(HALF_SLICE_CHICKEN3, - _MASKED_BIT_ENABLE(HSW_SAMPLE_C_PERFORMANCE)); + I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | + GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); /* WaSwitchSolVfFArbitrationPriority:hsw */ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); @@ -7282,32 +7186,11 @@ static void ivb_init_clock_gating(struct drm_i915_private *dev_priv) I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); - /* WaDisableEarlyCull:ivb */ - I915_WRITE(_3D_CHICKEN3, - _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL)); - /* WaDisableBackToBackFlipFix:ivb */ I915_WRITE(IVB_CHICKEN3, CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); - /* WaDisablePSDDualDispatchEnable:ivb */ - if (IS_IVB_GT1(dev_priv)) - I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, - _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); - - /* WaDisable_RenderCache_OperationalFlush:ivb */ - I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); - - /* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */ - I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, - GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); - - /* WaApplyL3ControlAndL3ChickenMode:ivb */ - I915_WRITE(GEN7_L3CNTLREG1, - GEN7_WA_FOR_GEN7_L3_CONTROL); - I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, - GEN7_WA_L3_CHICKEN_MODE); if (IS_IVB_GT1(dev_priv)) I915_WRITE(GEN7_ROW_CHICKEN2, _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); @@ -7319,10 +7202,6 @@ static void ivb_init_clock_gating(struct drm_i915_private *dev_priv) _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); } - /* WaForceL3Serialization:ivb */ - I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & - ~L3SQ_URB_READ_CAM_MATCH_DISABLE); - /* * According to the spec, bit 13 (RCZUNIT) must be set on IVB. * This implements the WaDisableRCZUnitClockGating:ivb workaround. @@ -7337,29 +7216,6 @@ static void ivb_init_clock_gating(struct drm_i915_private *dev_priv) g4x_disable_trickle_feed(dev_priv); - gen7_setup_fixed_func_scheduler(dev_priv); - - if (0) { /* causes HiZ corruption on ivb:gt1 */ - /* enable HiZ Raw Stall Optimization */ - I915_WRITE(CACHE_MODE_0_GEN7, - _MASKED_BIT_DISABLE(HIZ_RAW_STALL_OPT_DISABLE)); - } - - /* WaDisable4x2SubspanOptimization:ivb */ - I915_WRITE(CACHE_MODE_1, - _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); - - /* - * BSpec recommends 8x4 when MSAA is used, - * however in practice 16x4 seems fastest. - * - * Note that PS/WM thread counts depend on the WIZ hashing - * disable bit, which we don't touch here, but it's good - * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). - */ - I915_WRITE(GEN7_GT_MODE, - _MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4)); - snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); snpcr &= ~GEN6_MBC_SNPCR_MASK; snpcr |= GEN6_MBC_SNPCR_MED; @@ -7373,28 +7229,11 @@ static void ivb_init_clock_gating(struct drm_i915_private *dev_priv) static void vlv_init_clock_gating(struct drm_i915_private *dev_priv) { - /* WaDisableEarlyCull:vlv */ - I915_WRITE(_3D_CHICKEN3, - _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL)); - /* WaDisableBackToBackFlipFix:vlv */ I915_WRITE(IVB_CHICKEN3, CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); - /* WaPsdDispatchEnable:vlv */ - /* WaDisablePSDDualDispatchEnable:vlv */ - I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, - _MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP | - GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); - - /* WaDisable_RenderCache_OperationalFlush:vlv */ - I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); - - /* WaForceL3Serialization:vlv */ - I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & - ~L3SQ_URB_READ_CAM_MATCH_DISABLE); - /* WaDisableDopClockGating:vlv */ I915_WRITE(GEN7_ROW_CHICKEN2, _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); @@ -7404,8 +7243,6 @@ static void vlv_init_clock_gating(struct drm_i915_private *dev_priv) I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - gen7_setup_fixed_func_scheduler(dev_priv); - /* * According to the spec, bit 13 (RCZUNIT) must be set on IVB. * This implements the WaDisableRCZUnitClockGating:vlv workaround. @@ -7420,30 +7257,6 @@ static void vlv_init_clock_gating(struct drm_i915_private *dev_priv) I915_READ(GEN7_UCGCTL4) | GEN7_L3BANK2X_CLOCK_GATE_DISABLE); /* - * BSpec says this must be set, even though - * WaDisable4x2SubspanOptimization isn't listed for VLV. - */ - I915_WRITE(CACHE_MODE_1, - _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); - - /* - * BSpec recommends 8x4 when MSAA is used, - * however in practice 16x4 seems fastest. - * - * Note that PS/WM thread counts depend on the WIZ hashing - * disable bit, which we don't touch here, but it's good - * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM). - */ - I915_WRITE(GEN7_GT_MODE, - _MASKED_FIELD(GEN6_WIZ_HASHING_MASK, GEN6_WIZ_HASHING_16x4)); - - /* - * WaIncreaseL3CreditsForVLVB0:vlv - * This is the hardware default actually. - */ - I915_WRITE(GEN7_L3SQCREG1, VLV_B0_WA_L3SQCREG1_VALUE); - - /* * WaDisableVLVClockGating_VBIIssue:vlv * Disable clock gating on th GCFG unit to prevent a delay * in the reporting of vblank events. @@ -7495,13 +7308,6 @@ static void g4x_init_clock_gating(struct drm_i915_private *dev_priv) dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE; I915_WRITE(DSPCLK_GATE_D, dspclk_gate); - /* WaDisableRenderCachePipelinedFlush */ - I915_WRITE(CACHE_MODE_0, - _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); - - /* WaDisable_RenderCache_OperationalFlush:g4x */ - I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); - g4x_disable_trickle_feed(dev_priv); } @@ -7517,11 +7323,6 @@ static void i965gm_init_clock_gating(struct drm_i915_private *dev_priv) intel_uncore_write(uncore, MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); - - /* WaDisable_RenderCache_OperationalFlush:gen4 */ - intel_uncore_write(uncore, - CACHE_MODE_0, - _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); } static void i965g_init_clock_gating(struct drm_i915_private *dev_priv) @@ -7534,9 +7335,6 @@ static void i965g_init_clock_gating(struct drm_i915_private *dev_priv) I915_WRITE(RENCLK_GATE_D2, 0); I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); - - /* WaDisable_RenderCache_OperationalFlush:gen4 */ - I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE)); } static void gen3_init_clock_gating(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h index 6a2be7d0dd95..6090ce35226b 100644 --- a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h +++ b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h @@ -21,6 +21,7 @@ selftest(fence, i915_sw_fence_mock_selftests) selftest(scatterlist, scatterlist_mock_selftests) selftest(syncmap, i915_syncmap_mock_selftests) selftest(uncore, intel_uncore_mock_selftests) +selftest(ring, intel_ring_mock_selftests) selftest(engine, intel_engine_cs_mock_selftests) selftest(timelines, intel_timeline_mock_selftests) selftest(requests, i915_request_mock_selftests) diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index fe46c4bac64d..ec6c9ffbf35e 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -492,6 +492,7 @@ static void mtk_drm_crtc_hw_config(struct mtk_drm_crtc *mtk_crtc) cmdq_pkt_clear_event(cmdq_handle, mtk_crtc->cmdq_event); cmdq_pkt_wfe(cmdq_handle, mtk_crtc->cmdq_event); mtk_crtc_ddp_config(crtc, cmdq_handle); + cmdq_pkt_finalize(cmdq_handle); cmdq_pkt_flush_async(cmdq_handle, ddp_cmdq_cb, cmdq_handle); } #endif diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 4f932a419752..603b4a9969d3 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -34,7 +34,7 @@ struct stp_policy_node { unsigned int first_channel; unsigned int last_channel; /* this is the one that's exposed to the attributes */ - unsigned char priv[0]; + unsigned char priv[]; }; void *stp_policy_node_priv(struct stp_policy_node *pn) diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 3569439d53bb..a9be49fc7a6b 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -23,7 +23,7 @@ void *stp_policy_node_priv(struct stp_policy_node *pn); struct stp_master { unsigned int nr_free; - unsigned long chan_map[0]; + unsigned long chan_map[]; }; struct stm_device { @@ -42,7 +42,7 @@ struct stm_device { const struct config_item_type *pdrv_node_type; /* master allocation */ spinlock_t mc_lock; - struct stp_master *masters[0]; + struct stp_master *masters[]; }; #define to_stm_device(_d) \ diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index d1f278f73011..26f03a14a478 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -816,31 +816,6 @@ out_err_silent: EXPORT_SYMBOL_GPL(i2c_new_client_device); /** - * i2c_new_device - instantiate an i2c device - * @adap: the adapter managing the device - * @info: describes one I2C device; bus_num is ignored - * Context: can sleep - * - * This deprecated function has the same functionality as - * @i2c_new_client_device, it just returns NULL instead of an ERR_PTR in case of - * an error for compatibility with current I2C API. It will be removed once all - * users are converted. - * - * This returns the new i2c client, which may be saved for later use with - * i2c_unregister_device(); or NULL to indicate an error. - */ -struct i2c_client * -i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) -{ - struct i2c_client *ret; - - ret = i2c_new_client_device(adap, info); - return IS_ERR(ret) ? NULL : ret; -} -EXPORT_SYMBOL_GPL(i2c_new_device); - - -/** * i2c_unregister_device - reverse effect of i2c_new_*_device() * @client: value returned from i2c_new_*_device() * Context: can sleep diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index b34d2ff06931..56bb840142e3 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -4,7 +4,7 @@ * * This file contains the SMBus functions which are always included in the I2C * core because they can be emulated via I2C. SMBus specific extensions - * (e.g. smbalert) are handled in a seperate i2c-smbus module. + * (e.g. smbalert) are handled in a separate i2c-smbus module. * * All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> * SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 654252361653..13eacf6ab431 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -1021,7 +1021,7 @@ static int __init hp_sdc_register(void) hp_sdc.base_io = (unsigned long) 0xf0428000; hp_sdc.data_io = (unsigned long) hp_sdc.base_io + 1; hp_sdc.status_io = (unsigned long) hp_sdc.base_io + 3; - if (!probe_kernel_read(&i, (unsigned char *)hp_sdc.data_io, 1)) + if (!copy_from_kernel_nofault(&i, (unsigned char *)hp_sdc.data_io, 1)) hp_sdc.dev = (void *)1; hp_sdc.dev_err = hp_sdc_init(); #endif diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c index 7e3ebf6ed2cd..85de19fe9b6e 100644 --- a/drivers/irqchip/irq-ti-sci-inta.c +++ b/drivers/irqchip/irq-ti-sci-inta.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 Interrupt Aggregator irqchip driver * - * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018-2019 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> */ diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci-intr.c index 59d51a20bbd8..5ea148faf2ab 100644 --- a/drivers/irqchip/irq-ti-sci-intr.c +++ b/drivers/irqchip/irq-ti-sci-intr.c @@ -2,7 +2,7 @@ /* * Texas Instruments' K3 Interrupt Router irqchip driver * - * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018-2019 Texas Instruments Incorporated - https://www.ti.com/ * Lokesh Vutla <lokeshvutla@ti.com> */ diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 39de94edd73a..6548a601edf0 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -1389,7 +1389,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op, if (__set_blocks(n1, n1->keys + n2->keys, block_bytes(b->c)) > btree_blocks(new_nodes[i])) - goto out_nocoalesce; + goto out_unlock_nocoalesce; keys = n2->keys; /* Take the key of the node we're getting rid of */ @@ -1418,7 +1418,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op, if (__bch_keylist_realloc(&keylist, bkey_u64s(&new_nodes[i]->key))) - goto out_nocoalesce; + goto out_unlock_nocoalesce; bch_btree_node_write(new_nodes[i], &cl); bch_keylist_add(&keylist, &new_nodes[i]->key); @@ -1464,6 +1464,10 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op, /* Invalidated our iterator */ return -EINTR; +out_unlock_nocoalesce: + for (i = 0; i < nodes; i++) + mutex_unlock(&new_nodes[i]->write_lock); + out_nocoalesce: closure_sync(&cl); diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index f9975c22bf7e..2014016f9a60 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -19,6 +19,7 @@ #include <linux/genhd.h> #include <linux/idr.h> #include <linux/kthread.h> +#include <linux/workqueue.h> #include <linux/module.h> #include <linux/random.h> #include <linux/reboot.h> @@ -819,7 +820,8 @@ static void bcache_device_free(struct bcache_device *d) } static int bcache_device_init(struct bcache_device *d, unsigned int block_size, - sector_t sectors, make_request_fn make_request_fn) + sector_t sectors, make_request_fn make_request_fn, + struct block_device *cached_bdev) { struct request_queue *q; const size_t max_stripes = min_t(size_t, INT_MAX, @@ -885,6 +887,20 @@ static int bcache_device_init(struct bcache_device *d, unsigned int block_size, q->limits.io_min = block_size; q->limits.logical_block_size = block_size; q->limits.physical_block_size = block_size; + + if (q->limits.logical_block_size > PAGE_SIZE && cached_bdev) { + /* + * This should only happen with BCACHE_SB_VERSION_BDEV. + * Block/page size is checked for BCACHE_SB_VERSION_CDEV. + */ + pr_info("%s: sb/logical block size (%u) greater than page size (%lu) falling back to device logical block size (%u)\n", + d->disk->disk_name, q->limits.logical_block_size, + PAGE_SIZE, bdev_logical_block_size(cached_bdev)); + + /* This also adjusts physical block size/min io size if needed */ + blk_queue_logical_block_size(q, bdev_logical_block_size(cached_bdev)); + } + blk_queue_flag_set(QUEUE_FLAG_NONROT, d->disk->queue); blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, d->disk->queue); blk_queue_flag_set(QUEUE_FLAG_DISCARD, d->disk->queue); @@ -1340,7 +1356,7 @@ static int cached_dev_init(struct cached_dev *dc, unsigned int block_size) ret = bcache_device_init(&dc->disk, block_size, dc->bdev->bd_part->nr_sects - dc->sb.data_offset, - cached_dev_make_request); + cached_dev_make_request, dc->bdev); if (ret) return ret; @@ -1453,7 +1469,7 @@ static int flash_dev_run(struct cache_set *c, struct uuid_entry *u) kobject_init(&d->kobj, &bch_flash_dev_ktype); if (bcache_device_init(d, block_bytes(c), u->sectors, - flash_dev_make_request)) + flash_dev_make_request, NULL)) goto err; bcache_device_attach(d, c, u - c->uuids); @@ -2364,7 +2380,7 @@ static bool bch_is_open(struct block_device *bdev) } struct async_reg_args { - struct work_struct reg_work; + struct delayed_work reg_work; char *path; struct cache_sb *sb; struct cache_sb_disk *sb_disk; @@ -2375,7 +2391,7 @@ static void register_bdev_worker(struct work_struct *work) { int fail = false; struct async_reg_args *args = - container_of(work, struct async_reg_args, reg_work); + container_of(work, struct async_reg_args, reg_work.work); struct cached_dev *dc; dc = kzalloc(sizeof(*dc), GFP_KERNEL); @@ -2405,7 +2421,7 @@ static void register_cache_worker(struct work_struct *work) { int fail = false; struct async_reg_args *args = - container_of(work, struct async_reg_args, reg_work); + container_of(work, struct async_reg_args, reg_work.work); struct cache *ca; ca = kzalloc(sizeof(*ca), GFP_KERNEL); @@ -2433,11 +2449,12 @@ out: static void register_device_aync(struct async_reg_args *args) { if (SB_IS_BDEV(args->sb)) - INIT_WORK(&args->reg_work, register_bdev_worker); + INIT_DELAYED_WORK(&args->reg_work, register_bdev_worker); else - INIT_WORK(&args->reg_work, register_cache_worker); + INIT_DELAYED_WORK(&args->reg_work, register_cache_worker); - queue_work(system_wq, &args->reg_work); + /* 10 jiffies is enough for a delay */ + queue_delayed_work(system_wq, &args->reg_work, 10); } static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, diff --git a/drivers/media/usb/pwc/pwc.h b/drivers/media/usb/pwc/pwc.h index 3362962d0d00..b02a3c7b7742 100644 --- a/drivers/media/usb/pwc/pwc.h +++ b/drivers/media/usb/pwc/pwc.h @@ -193,7 +193,7 @@ struct pwc_raw_frame { decompressor) */ __u8 cmd[4]; /* the four byte of the command (in case of nala, only the first 3 bytes is filled) */ - __u8 rawframe[0]; /* frame_size = H / 4 * vbandlength */ + __u8 rawframe[]; /* frame_size = H / 4 * vbandlength */ } __packed; /* intermediate buffers with raw data from the USB cam */ diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 04368ee2a809..d46f21db9b1f 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -5,6 +5,12 @@ menuconfig MEMORY bool "Memory Controller drivers" + help + This option allows to enable specific memory controller drivers, + useful mostly on embedded systems. These could be controllers + for DRAM (SDR, DDR), ROM, SRAM and others. The drivers features + vary from memory tuning and frequency scaling to enabling + access to attached peripherals through memory bus. if MEMORY diff --git a/drivers/memory/brcmstb_dpfe.c b/drivers/memory/brcmstb_dpfe.c index 82b415be18d1..60e8633b1175 100644 --- a/drivers/memory/brcmstb_dpfe.c +++ b/drivers/memory/brcmstb_dpfe.c @@ -23,7 +23,7 @@ * - BE kernel + LE firmware image * - BE kernel + BE firmware image * - * The DPCU always runs in big endian mode. The firwmare image, however, can + * The DPCU always runs in big endian mode. The firmware image, however, can * be in either format. Also, communication between host CPU and DCPU is * always in little endian. */ @@ -188,7 +188,7 @@ struct brcmstb_dpfe_priv { struct mutex lock; }; -static const char *error_text[] = { +static const char * const error_text[] = { "Success", "Header code incorrect", "Unknown command or argument", "Incorrect checksum", "Malformed command", "Timed out", }; @@ -379,9 +379,8 @@ static void __iomem *get_msg_ptr(struct brcmstb_dpfe_priv *priv, u32 response, void __iomem *ptr = NULL; /* There is no need to use this function for API v3 or later. */ - if (unlikely(priv->dpfe_api->version >= 3)) { + if (unlikely(priv->dpfe_api->version >= 3)) return NULL; - } msg_type = (response >> DRAM_MSG_TYPE_OFFSET) & DRAM_MSG_TYPE_MASK; offset = (response >> DRAM_MSG_ADDR_OFFSET) & DRAM_MSG_ADDR_MASK; diff --git a/drivers/memory/bt1-l2-ctl.c b/drivers/memory/bt1-l2-ctl.c index 633fea6a4edf..85965fa26e0b 100644 --- a/drivers/memory/bt1-l2-ctl.c +++ b/drivers/memory/bt1-l2-ctl.c @@ -66,6 +66,7 @@ struct l2_ctl_device_attribute { struct device_attribute dev_attr; enum l2_ctl_stall id; }; + #define to_l2_ctl_dev_attr(_dev_attr) \ container_of(_dev_attr, struct l2_ctl_device_attribute, dev_attr) @@ -242,6 +243,7 @@ static ssize_t l2_ctl_latency_store(struct device *dev, return count; } + static L2_CTL_ATTR_RW(l2_ws_latency, l2_ctl_latency, L2_WS_STALL); static L2_CTL_ATTR_RW(l2_tag_latency, l2_ctl_latency, L2_TAG_STALL); static L2_CTL_ATTR_RW(l2_data_latency, l2_ctl_latency, L2_DATA_STALL); diff --git a/drivers/memory/da8xx-ddrctl.c b/drivers/memory/da8xx-ddrctl.c index e8f9b3f461f5..872addd0ec60 100644 --- a/drivers/memory/da8xx-ddrctl.c +++ b/drivers/memory/da8xx-ddrctl.c @@ -102,14 +102,12 @@ static int da8xx_ddrctl_probe(struct platform_device *pdev) { const struct da8xx_ddrctl_config_knob *knob; const struct da8xx_ddrctl_setting *setting; - struct device_node *node; struct resource *res; void __iomem *ddrctl; struct device *dev; u32 reg; dev = &pdev->dev; - node = dev->of_node; setting = da8xx_ddrctl_get_board_settings(); if (!setting) { diff --git a/drivers/memory/emif-asm-offsets.c b/drivers/memory/emif-asm-offsets.c index db8043019ec6..4b98d1854cd7 100644 --- a/drivers/memory/emif-asm-offsets.c +++ b/drivers/memory/emif-asm-offsets.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * TI AM33XX EMIF PM Assembly Offsets * * Copyright (C) 2016-2017 Texas Instruments Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/ti-emif-sram.h> diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index 9d9127bf2a59..bb6a71d26798 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -282,10 +282,9 @@ static void set_lpmode(struct emif_data *emif, u8 lpmode) * the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field to 0x4. */ if ((emif->plat_data->ip_rev == EMIF_4D) && - (EMIF_LP_MODE_PWR_DN == lpmode)) { + (lpmode == EMIF_LP_MODE_PWR_DN)) { WARN_ONCE(1, - "REG_LP_MODE = LP_MODE_PWR_DN(4) is prohibited by" - "erratum i743 switch to LP_MODE_SELF_REFRESH(2)\n"); + "REG_LP_MODE = LP_MODE_PWR_DN(4) is prohibited by erratum i743 switch to LP_MODE_SELF_REFRESH(2)\n"); /* rollback LP_MODE to Self-refresh mode */ lpmode = EMIF_LP_MODE_SELF_REFRESH; } @@ -714,7 +713,7 @@ static u32 get_ext_phy_ctrl_2_intelliphy_4d5(void) u32 fifo_we_slave_ratio; fifo_we_slave_ratio = DIV_ROUND_CLOSEST( - EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck); + EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256, t_ck); return fifo_we_slave_ratio | fifo_we_slave_ratio << 11 | fifo_we_slave_ratio << 22; @@ -725,7 +724,7 @@ static u32 get_ext_phy_ctrl_3_intelliphy_4d5(void) u32 fifo_we_slave_ratio; fifo_we_slave_ratio = DIV_ROUND_CLOSEST( - EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck); + EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256, t_ck); return fifo_we_slave_ratio >> 10 | fifo_we_slave_ratio << 1 | fifo_we_slave_ratio << 12 | fifo_we_slave_ratio << 23; @@ -736,7 +735,7 @@ static u32 get_ext_phy_ctrl_4_intelliphy_4d5(void) u32 fifo_we_slave_ratio; fifo_we_slave_ratio = DIV_ROUND_CLOSEST( - EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256 , t_ck); + EMIF_INTELLI_PHY_DQS_GATE_OPENING_DELAY_PS * 256, t_ck); return fifo_we_slave_ratio >> 9 | fifo_we_slave_ratio << 2 | fifo_we_slave_ratio << 13; @@ -975,8 +974,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif) EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART)) { if (emif->temperature_level >= SDRAM_TEMP_HIGH_DERATE_REFRESH) { dev_err(emif->dev, - "%s:NOT Extended temperature capable memory." - "Converting MR4=0x%02x as shutdown event\n", + "%s:NOT Extended temperature capable memory. Converting MR4=0x%02x as shutdown event\n", __func__, emif->temperature_level); /* * Temperature far too high - do kernel_power_off() @@ -1318,9 +1316,9 @@ static void __init_or_module of_get_ddr_info(struct device_node *np_emif, if (of_find_property(np_emif, "cal-resistor-per-cs", &len)) dev_info->cal_resistors_per_cs = true; - if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s4")) + if (of_device_is_compatible(np_ddr, "jedec,lpddr2-s4")) dev_info->type = DDR_TYPE_LPDDR2_S4; - else if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s2")) + else if (of_device_is_compatible(np_ddr, "jedec,lpddr2-s2")) dev_info->type = DDR_TYPE_LPDDR2_S2; of_property_read_u32(np_ddr, "density", &density); @@ -1563,11 +1561,8 @@ static int __init_or_module emif_probe(struct platform_device *pdev) goto error; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(emif->dev, "%s: error getting IRQ resource - %d\n", - __func__, irq); + if (irq < 0) goto error; - } emif_onetime_settings(emif); emif_debugfs_init(emif); diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c index a2c971743ffe..89f99b5b6450 100644 --- a/drivers/memory/fsl_ifc.c +++ b/drivers/memory/fsl_ifc.c @@ -53,6 +53,7 @@ int fsl_ifc_find(phys_addr_t addr_base) for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr); + if (cspr & CSPR_V && (cspr & CSPR_BA) == convert_ifc_address(addr_base)) return i; @@ -153,8 +154,8 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) /* read for chip select error */ cs_err = ifc_in32(&ifc->cm_evter_stat); if (cs_err) { - dev_err(ctrl->dev, "transaction sent to IFC is not mapped to" - "any memory bank 0x%08X\n", cs_err); + dev_err(ctrl->dev, "transaction sent to IFC is not mapped to any memory bank 0x%08X\n", + cs_err); /* clear the chip select error */ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); @@ -163,24 +164,24 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) err_addr = ifc_in32(&ifc->cm_erattr1); if (status & IFC_CM_ERATTR0_ERTYP_READ) - dev_err(ctrl->dev, "Read transaction error" - "CM_ERATTR0 0x%08X\n", status); + dev_err(ctrl->dev, "Read transaction error CM_ERATTR0 0x%08X\n", + status); else - dev_err(ctrl->dev, "Write transaction error" - "CM_ERATTR0 0x%08X\n", status); + dev_err(ctrl->dev, "Write transaction error CM_ERATTR0 0x%08X\n", + status); err_axiid = (status & IFC_CM_ERATTR0_ERAID) >> IFC_CM_ERATTR0_ERAID_SHIFT; - dev_err(ctrl->dev, "AXI ID of the error" - "transaction 0x%08X\n", err_axiid); + dev_err(ctrl->dev, "AXI ID of the error transaction 0x%08X\n", + err_axiid); err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >> IFC_CM_ERATTR0_ESRCID_SHIFT; - dev_err(ctrl->dev, "SRC ID of the error" - "transaction 0x%08X\n", err_srcid); + dev_err(ctrl->dev, "SRC ID of the error transaction 0x%08X\n", + err_srcid); - dev_err(ctrl->dev, "Transaction Address corresponding to error" - "ERADDR 0x%08X\n", err_addr); + dev_err(ctrl->dev, "Transaction Address corresponding to error ERADDR 0x%08X\n", + err_addr); ret = IRQ_HANDLED; } @@ -199,7 +200,7 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) * the resources needed for the controller only. The * resources for the NAND banks themselves are allocated * in the chip probe function. -*/ + */ static int fsl_ifc_ctrl_probe(struct platform_device *dev) { int ret = 0; @@ -250,8 +251,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) /* get the Controller level irq */ fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); if (fsl_ifc_ctrl_dev->irq == 0) { - dev_err(&dev->dev, "failed to get irq resource " - "for IFC\n"); + dev_err(&dev->dev, "failed to get irq resource for IFC\n"); ret = -ENODEV; goto err; } diff --git a/drivers/memory/jz4780-nemc.c b/drivers/memory/jz4780-nemc.c index b232ed279fc3..3ec5cb0fce1e 100644 --- a/drivers/memory/jz4780-nemc.c +++ b/drivers/memory/jz4780-nemc.c @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/init.h> +#include <linux/io.h> #include <linux/math64.h> #include <linux/of.h> #include <linux/of_address.h> @@ -22,6 +23,8 @@ #define NEMC_SMCRn(n) (0x14 + (((n) - 1) * 4)) #define NEMC_NFCSR 0x50 +#define NEMC_REG_LEN 0x54 + #define NEMC_SMCR_SMT BIT(0) #define NEMC_SMCR_BW_SHIFT 6 #define NEMC_SMCR_BW_MASK (0x3 << NEMC_SMCR_BW_SHIFT) @@ -288,7 +291,19 @@ static int jz4780_nemc_probe(struct platform_device *pdev) nemc->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nemc->base = devm_ioremap_resource(dev, res); + + /* + * The driver currently only uses the registers up to offset + * NEMC_REG_LEN. Since the EFUSE registers are in the middle of the + * NEMC registers, we only request the registers we will use for now; + * that way the EFUSE driver can probe too. + */ + if (!devm_request_mem_region(dev, res->start, NEMC_REG_LEN, dev_name(dev))) { + dev_err(dev, "unable to request I/O memory region\n"); + return -EBUSY; + } + + nemc->base = devm_ioremap(dev, res->start, NEMC_REG_LEN); if (IS_ERR(nemc->base)) { dev_err(dev, "failed to get I/O memory\n"); return PTR_ERR(nemc->base); diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index a113e811faab..e154bea3cf14 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -60,7 +60,7 @@ struct mtk_smi_common_plat { struct mtk_smi_larb_gen { int port_in_larb[MTK_LARB_NR_MAX + 1]; - void (*config_port)(struct device *); + void (*config_port)(struct device *dev); unsigned int larb_direct_to_common_mask; bool has_gals; }; diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c index 886aea587276..8450638e8670 100644 --- a/drivers/memory/mvebu-devbus.c +++ b/drivers/memory/mvebu-devbus.c @@ -124,32 +124,32 @@ static int devbus_get_timing_params(struct devbus *devbus, * The bus width is encoded into the register as 0 for 8 bits, * and 1 for 16 bits, so we do the necessary conversion here. */ - if (r->bus_width == 8) + if (r->bus_width == 8) { r->bus_width = 0; - else if (r->bus_width == 16) + } else if (r->bus_width == 16) { r->bus_width = 1; - else { + } else { dev_err(devbus->dev, "invalid bus width %d\n", r->bus_width); return -EINVAL; } err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps", - &r->badr_skew); + &r->badr_skew); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps", - &r->turn_off); + &r->turn_off); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps", - &r->acc_first); + &r->acc_first); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps", - &r->acc_next); + &r->acc_next); if (err < 0) return err; @@ -175,17 +175,17 @@ static int devbus_get_timing_params(struct devbus *devbus, } err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps", - &w->ale_wr); + &w->ale_wr); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps", - &w->wr_low); + &w->wr_low); if (err < 0) return err; err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps", - &w->wr_high); + &w->wr_high); if (err < 0) return err; diff --git a/drivers/memory/of_memory.c b/drivers/memory/of_memory.c index 71f26eac7350..d9f5437d3bce 100644 --- a/drivers/memory/of_memory.c +++ b/drivers/memory/of_memory.c @@ -4,11 +4,10 @@ * * Copyright (C) 2012 Texas Instruments, Inc. * Copyright (C) 2019 Samsung Electronics Co., Ltd. + * Copyright (C) 2020 Krzysztof Kozlowski <krzk@kernel.org> */ #include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/list.h> #include <linux/of.h> #include <linux/gfp.h> #include <linux/export.h> @@ -19,7 +18,7 @@ /** * of_get_min_tck() - extract min timing values for ddr * @np: pointer to ddr device tree node - * @device: device requesting for min timing values + * @dev: device requesting for min timing values * * Populates the lpddr2_min_tck structure by extracting data * from device tree node. Returns a pointer to the populated @@ -27,7 +26,7 @@ * default min timings provided by JEDEC. */ const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np, - struct device *dev) + struct device *dev) { int ret = 0; struct lpddr2_min_tck *min; @@ -56,13 +55,13 @@ const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np, return min; default_min_tck: - dev_warn(dev, "%s: using default min-tck values\n", __func__); + dev_warn(dev, "Using default min-tck values\n"); return &lpddr2_jedec_min_tck; } EXPORT_SYMBOL(of_get_min_tck); static int of_do_get_timings(struct device_node *np, - struct lpddr2_timings *tim) + struct lpddr2_timings *tim) { int ret; @@ -84,7 +83,7 @@ static int of_do_get_timings(struct device_node *np, ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit); ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns); ret |= of_property_read_u32(np, "tDQSCK-max-derated", - &tim->tDQSCK_max_derated); + &tim->tDQSCK_max_derated); return ret; } @@ -103,7 +102,9 @@ static int of_do_get_timings(struct device_node *np, * while populating, returns default timings provided by JEDEC. */ const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr, - struct device *dev, u32 device_type, u32 *nr_frequencies) + struct device *dev, + u32 device_type, + u32 *nr_frequencies) { struct lpddr2_timings *timings = NULL; u32 arr_sz = 0, i = 0; @@ -116,7 +117,7 @@ const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr, tim_compat = "jedec,lpddr2-timings"; break; default: - dev_warn(dev, "%s: un-supported memory type\n", __func__); + dev_warn(dev, "Unsupported memory type\n"); } for_each_child_of_node(np_ddr, np_tim) @@ -145,7 +146,7 @@ const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr, return timings; default_timings: - dev_warn(dev, "%s: using default timings\n", __func__); + dev_warn(dev, "Using default memory timings\n"); *nr_frequencies = ARRAY_SIZE(lpddr2_jedec_timings); return lpddr2_jedec_timings; } @@ -154,7 +155,7 @@ EXPORT_SYMBOL(of_get_ddr_timings); /** * of_lpddr3_get_min_tck() - extract min timing values for lpddr3 * @np: pointer to ddr device tree node - * @device: device requesting for min timing values + * @dev: device requesting for min timing values * * Populates the lpddr3_min_tck structure by extracting data * from device tree node. Returns a pointer to the populated @@ -193,8 +194,7 @@ const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np, ret |= of_property_read_u32(np, "tMRD-min-tck", &min->tMRD); if (ret) { - dev_warn(dev, "%s: errors while parsing min-tck values\n", - __func__); + dev_warn(dev, "Errors while parsing min-tck values\n"); devm_kfree(dev, min); goto default_min_tck; } @@ -202,7 +202,7 @@ const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np, return min; default_min_tck: - dev_warn(dev, "%s: using default min-tck values\n", __func__); + dev_warn(dev, "Using default min-tck values\n"); return NULL; } EXPORT_SYMBOL(of_lpddr3_get_min_tck); @@ -264,7 +264,7 @@ const struct lpddr3_timings tim_compat = "jedec,lpddr3-timings"; break; default: - dev_warn(dev, "%s: un-supported memory type\n", __func__); + dev_warn(dev, "Unsupported memory type\n"); } for_each_child_of_node(np_ddr, np_tim) @@ -293,7 +293,7 @@ const struct lpddr3_timings return timings; default_timings: - dev_warn(dev, "%s: failed to get timings\n", __func__); + dev_warn(dev, "Failed to get timings\n"); *nr_frequencies = 0; return NULL; } diff --git a/drivers/memory/of_memory.h b/drivers/memory/of_memory.h index e39ecc4c733d..4a99b232ab0a 100644 --- a/drivers/memory/of_memory.h +++ b/drivers/memory/of_memory.h @@ -3,22 +3,23 @@ * OpenFirmware helpers for memory drivers * * Copyright (C) 2012 Texas Instruments, Inc. + * Copyright (C) 2020 Krzysztof Kozlowski <krzk@kernel.org> */ #ifndef __LINUX_MEMORY_OF_REG_H #define __LINUX_MEMORY_OF_REG_H #if defined(CONFIG_OF) && defined(CONFIG_DDR) -extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np, - struct device *dev); -extern const struct lpddr2_timings - *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev, - u32 device_type, u32 *nr_frequencies); -extern const struct lpddr3_min_tck - *of_lpddr3_get_min_tck(struct device_node *np, struct device *dev); -extern const struct lpddr3_timings - *of_lpddr3_get_ddr_timings(struct device_node *np_ddr, - struct device *dev, u32 device_type, u32 *nr_frequencies); +const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np, + struct device *dev); +const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr, + struct device *dev, + u32 device_type, u32 *nr_frequencies); +const struct lpddr3_min_tck *of_lpddr3_get_min_tck(struct device_node *np, + struct device *dev); +const struct lpddr3_timings * +of_lpddr3_get_ddr_timings(struct device_node *np_ddr, + struct device *dev, u32 device_type, u32 *nr_frequencies); #else static inline const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np, struct device *dev) diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index eff26c1b1394..f512cbc7a36c 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -29,6 +29,7 @@ #include <linux/of_platform.h> #include <linux/omap-gpmc.h> #include <linux/pm_runtime.h> +#include <linux/sizes.h> #include <linux/platform_data/mtd-nand-omap2.h> @@ -108,8 +109,8 @@ #define ENABLE_PREFETCH (0x1 << 7) #define DMA_MPU_MODE 2 -#define GPMC_REVISION_MAJOR(l) ((l >> 4) & 0xf) -#define GPMC_REVISION_MINOR(l) (l & 0xf) +#define GPMC_REVISION_MAJOR(l) (((l) >> 4) & 0xf) +#define GPMC_REVISION_MINOR(l) ((l) & 0xf) #define GPMC_HAS_WR_ACCESS 0x1 #define GPMC_HAS_WR_DATA_MUX_BUS 0x2 @@ -140,27 +141,27 @@ #define GPMC_CONFIG1_WRITEMULTIPLE_SUPP (1 << 28) #define GPMC_CONFIG1_WRITETYPE_ASYNC (0 << 27) #define GPMC_CONFIG1_WRITETYPE_SYNC (1 << 27) -#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) ((val & 3) << 25) +#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) (((val) & 3) << 25) /** CLKACTIVATIONTIME Max Ticks */ #define GPMC_CONFIG1_CLKACTIVATIONTIME_MAX 2 -#define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23) +#define GPMC_CONFIG1_PAGE_LEN(val) (((val) & 3) << 23) /** ATTACHEDDEVICEPAGELENGTH Max Value */ #define GPMC_CONFIG1_ATTACHEDDEVICEPAGELENGTH_MAX 2 #define GPMC_CONFIG1_WAIT_READ_MON (1 << 22) #define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21) -#define GPMC_CONFIG1_WAIT_MON_TIME(val) ((val & 3) << 18) +#define GPMC_CONFIG1_WAIT_MON_TIME(val) (((val) & 3) << 18) /** WAITMONITORINGTIME Max Ticks */ #define GPMC_CONFIG1_WAITMONITORINGTIME_MAX 2 -#define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16) -#define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12) +#define GPMC_CONFIG1_WAIT_PIN_SEL(val) (((val) & 3) << 16) +#define GPMC_CONFIG1_DEVICESIZE(val) (((val) & 3) << 12) #define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) /** DEVICESIZE Max Value */ #define GPMC_CONFIG1_DEVICESIZE_MAX 1 -#define GPMC_CONFIG1_DEVICETYPE(val) ((val & 3) << 10) +#define GPMC_CONFIG1_DEVICETYPE(val) (((val) & 3) << 10) #define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0) -#define GPMC_CONFIG1_MUXTYPE(val) ((val & 3) << 8) +#define GPMC_CONFIG1_MUXTYPE(val) (((val) & 3) << 8) #define GPMC_CONFIG1_TIME_PARA_GRAN (1 << 4) -#define GPMC_CONFIG1_FCLK_DIV(val) (val & 3) +#define GPMC_CONFIG1_FCLK_DIV(val) ((val) & 3) #define GPMC_CONFIG1_FCLK_DIV2 (GPMC_CONFIG1_FCLK_DIV(1)) #define GPMC_CONFIG1_FCLK_DIV3 (GPMC_CONFIG1_FCLK_DIV(2)) #define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3)) @@ -245,7 +246,7 @@ static DEFINE_SPINLOCK(gpmc_mem_lock); static unsigned int gpmc_cs_num = GPMC_CS_NUM; static unsigned int gpmc_nr_waitpins; static resource_size_t phys_base, mem_size; -static unsigned gpmc_capability; +static unsigned int gpmc_capability; static void __iomem *gpmc_base; static struct clk *gpmc_l3_clk; @@ -291,15 +292,14 @@ static unsigned long gpmc_get_fclk_period(void) /** * gpmc_get_clk_period - get period of selected clock domain in ps - * @cs Chip Select Region. - * @cd Clock Domain. + * @cs: Chip Select Region. + * @cd: Clock Domain. * * GPMC_CS_CONFIG1 GPMCFCLKDIVIDER for cs has to be setup * prior to calling this function with GPMC_CD_CLK. */ static unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd) { - unsigned long tick_ps = gpmc_get_fclk_period(); u32 l; int div; @@ -319,7 +319,6 @@ static unsigned long gpmc_get_clk_period(int cs, enum gpmc_clk_domain cd) } return tick_ps; - } static unsigned int gpmc_ns_to_clk_ticks(unsigned int time_ns, int cs, @@ -411,7 +410,7 @@ static void gpmc_cs_bool_timings(int cs, const struct gpmc_bool_timings *p) * @reg: GPMC_CS_CONFIGn register offset. * @st_bit: Start Bit * @end_bit: End Bit. Must be >= @st_bit. - * @ma:x Maximum parameter value (before optional @shift). + * @max: Maximum parameter value (before optional @shift). * If 0, maximum is as high as @st_bit and @end_bit allow. * @name: DTS node name, w/o "gpmc," * @cd: Clock Domain of timing parameter. @@ -511,7 +510,7 @@ static void gpmc_cs_show_timings(int cs, const char *desc) GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 4, 4, "time-para-granularity"); GPMC_GET_RAW(GPMC_CS_CONFIG1, 8, 9, "mux-add-data"); GPMC_GET_RAW_SHIFT_MAX(GPMC_CS_CONFIG1, 12, 13, 1, - GPMC_CONFIG1_DEVICESIZE_MAX, "device-width"); + GPMC_CONFIG1_DEVICESIZE_MAX, "device-width"); GPMC_GET_RAW(GPMC_CS_CONFIG1, 16, 17, "wait-pin"); GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 21, 21, "wait-on-write"); GPMC_GET_RAW_BOOL(GPMC_CS_CONFIG1, 22, 22, "wait-on-read"); @@ -625,9 +624,8 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max l = gpmc_cs_read_reg(cs, reg); #ifdef CONFIG_OMAP_GPMC_DEBUG - pr_info( - "GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n", - cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000, + pr_info("GPMC CS%d: %-17s: %3d ticks, %3lu ns (was %3i ticks) %3d ns\n", + cs, name, ticks, gpmc_get_clk_period(cs, cd) * ticks / 1000, (l >> st_bit) & mask, time); #endif l &= ~(mask << st_bit); @@ -662,7 +660,6 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, int max */ static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring) { - int div = gpmc_ns_to_ticks(wait_monitoring); div += GPMC_CONFIG1_WAITMONITORINGTIME_MAX - 1; @@ -674,7 +671,6 @@ static int gpmc_calc_waitmonitoring_divider(unsigned int wait_monitoring) div = 1; return div; - } /** @@ -728,7 +724,6 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t, if (!s->sync_read && !s->sync_write && (s->wait_on_read || s->wait_on_write) ) { - div = gpmc_calc_waitmonitoring_divider(t->wait_monitoring); if (div < 0) { pr_err("%s: waitmonitoringtime %3d ns too large for greatest gpmcfclkdivider.\n", @@ -958,7 +953,7 @@ static int gpmc_cs_remap(int cs, u32 base) * Make sure we ignore any device offsets from the GPMC partition * allocated for the chip select and that the new base confirms * to the GPMC 16MB minimum granularity. - */ + */ base &= ~(SZ_16M - 1); gpmc_cs_get_memconf(cs, &old_base, &size); @@ -1087,7 +1082,7 @@ static struct gpmc_nand_ops nand_ops = { /** * gpmc_omap_get_nand_ops - Get the GPMC NAND interface - * @regs: the GPMC NAND register map exclusive for NAND use. + * @reg: the GPMC NAND register map exclusive for NAND use. * @cs: GPMC chip select number on which the NAND sits. The * register map returned will be specific to this chip select. * @@ -1242,7 +1237,7 @@ int gpmc_omap_onenand_set_timings(struct device *dev, int cs, int freq, } EXPORT_SYMBOL_GPL(gpmc_omap_onenand_set_timings); -int gpmc_get_client_irq(unsigned irq_config) +int gpmc_get_client_irq(unsigned int irq_config) { if (!gpmc_irq_domain) { pr_warn("%s called before GPMC IRQ domain available\n", @@ -1465,7 +1460,6 @@ static void gpmc_mem_exit(void) continue; gpmc_cs_delete_mem(cs); } - } static void gpmc_mem_init(void) @@ -1634,17 +1628,14 @@ static int gpmc_calc_async_read_timings(struct gpmc_timings *gpmc_t, /* oe_on */ temp = dev_t->t_oeasu; if (mux) - temp = max_t(u32, temp, - gpmc_t->adv_rd_off + dev_t->t_aavdh); + temp = max_t(u32, temp, gpmc_t->adv_rd_off + dev_t->t_aavdh); gpmc_t->oe_on = gpmc_round_ps_to_ticks(temp); /* access */ temp = max_t(u32, dev_t->t_iaa, /* XXX: remove t_iaa in async ? */ - gpmc_t->oe_on + dev_t->t_oe); - temp = max_t(u32, temp, - gpmc_t->cs_on + dev_t->t_ce); - temp = max_t(u32, temp, - gpmc_t->adv_on + dev_t->t_aa); + gpmc_t->oe_on + dev_t->t_oe); + temp = max_t(u32, temp, gpmc_t->cs_on + dev_t->t_ce); + temp = max_t(u32, temp, gpmc_t->adv_on + dev_t->t_aa); gpmc_t->access = gpmc_round_ps_to_ticks(temp); gpmc_t->oe_off = gpmc_t->access + gpmc_ticks_to_ps(1); @@ -1753,10 +1744,11 @@ static int gpmc_calc_common_timings(struct gpmc_timings *gpmc_t, return 0; } -/* TODO: remove this function once all peripherals are confirmed to +/* + * TODO: remove this function once all peripherals are confirmed to * work with generic timing. Simultaneously gpmc_cs_set_timings() * has to be modified to handle timings in ps instead of ns -*/ + */ static void gpmc_convert_ps_to_ns(struct gpmc_timings *t) { t->cs_on /= 1000; @@ -2089,7 +2081,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, gpmc_cs_disable_mem(cs); /* - * FIXME: gpmc_cs_request() will map the CS to an arbitary + * FIXME: gpmc_cs_request() will map the CS to an arbitrary * location in the gpmc address space. When booting with * device-tree we want the NOR flash to be mapped to the * location specified in the device-tree blob. So remap the diff --git a/drivers/memory/pl172.c b/drivers/memory/pl172.c index ff57195b4e37..575fadbffa30 100644 --- a/drivers/memory/pl172.c +++ b/drivers/memory/pl172.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Memory controller driver for ARM PrimeCell PL172 * PrimeCell MultiPort Memory Controller (PL172) @@ -6,10 +7,6 @@ * * Based on: * TI AEMIF driver, Copyright (C) 2010 - 2013 Texas Instruments Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/amba/bus.h> @@ -24,7 +21,7 @@ #include <linux/of_platform.h> #include <linux/time.h> -#define MPMC_STATIC_CFG(n) (0x200 + 0x20 * n) +#define MPMC_STATIC_CFG(n) (0x200 + 0x20 * (n)) #define MPMC_STATIC_CFG_MW_8BIT 0x0 #define MPMC_STATIC_CFG_MW_16BIT 0x1 #define MPMC_STATIC_CFG_MW_32BIT 0x2 @@ -34,17 +31,17 @@ #define MPMC_STATIC_CFG_EW BIT(8) #define MPMC_STATIC_CFG_B BIT(19) #define MPMC_STATIC_CFG_P BIT(20) -#define MPMC_STATIC_WAIT_WEN(n) (0x204 + 0x20 * n) +#define MPMC_STATIC_WAIT_WEN(n) (0x204 + 0x20 * (n)) #define MPMC_STATIC_WAIT_WEN_MAX 0x0f -#define MPMC_STATIC_WAIT_OEN(n) (0x208 + 0x20 * n) +#define MPMC_STATIC_WAIT_OEN(n) (0x208 + 0x20 * (n)) #define MPMC_STATIC_WAIT_OEN_MAX 0x0f -#define MPMC_STATIC_WAIT_RD(n) (0x20c + 0x20 * n) +#define MPMC_STATIC_WAIT_RD(n) (0x20c + 0x20 * (n)) #define MPMC_STATIC_WAIT_RD_MAX 0x1f -#define MPMC_STATIC_WAIT_PAGE(n) (0x210 + 0x20 * n) +#define MPMC_STATIC_WAIT_PAGE(n) (0x210 + 0x20 * (n)) #define MPMC_STATIC_WAIT_PAGE_MAX 0x1f -#define MPMC_STATIC_WAIT_WR(n) (0x214 + 0x20 * n) +#define MPMC_STATIC_WAIT_WR(n) (0x214 + 0x20 * (n)) #define MPMC_STATIC_WAIT_WR_MAX 0x1f -#define MPMC_STATIC_WAIT_TURN(n) (0x218 + 0x20 * n) +#define MPMC_STATIC_WAIT_TURN(n) (0x218 + 0x20 * (n)) #define MPMC_STATIC_WAIT_TURN_MAX 0x0f /* Maximum number of static chip selects */ diff --git a/drivers/memory/samsung/Kconfig b/drivers/memory/samsung/Kconfig index 20a8406ce786..8e240f078afc 100644 --- a/drivers/memory/samsung/Kconfig +++ b/drivers/memory/samsung/Kconfig @@ -23,5 +23,12 @@ config EXYNOS5422_DMC config EXYNOS_SROM bool "Exynos SROM controller driver" if COMPILE_TEST depends on (ARM && ARCH_EXYNOS) || (COMPILE_TEST && HAS_IOMEM) + help + This adds driver for Samsung Exynos SoC SROM controller. The driver + in basic operation mode only saves and restores SROM registers + during suspend. If however appropriate device tree configuration + is provided, the driver enables support for external memory + or external devices. + If unsure, say Y on devices with Samsung Exynos SocS. endif diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c index 6510d7bab217..e73dd330af47 100644 --- a/drivers/memory/samsung/exynos-srom.c +++ b/drivers/memory/samsung/exynos-srom.c @@ -47,9 +47,9 @@ struct exynos_srom { struct exynos_srom_reg_dump *reg_offset; }; -static struct exynos_srom_reg_dump *exynos_srom_alloc_reg_dump( - const unsigned long *rdump, - unsigned long nr_rdump) +static struct exynos_srom_reg_dump * +exynos_srom_alloc_reg_dump(const unsigned long *rdump, + unsigned long nr_rdump) { struct exynos_srom_reg_dump *rd; unsigned int i; @@ -116,7 +116,7 @@ static int exynos_srom_probe(struct platform_device *pdev) } srom = devm_kzalloc(&pdev->dev, - sizeof(struct exynos_srom), GFP_KERNEL); + sizeof(struct exynos_srom), GFP_KERNEL); if (!srom) return -ENOMEM; @@ -130,7 +130,7 @@ static int exynos_srom_probe(struct platform_device *pdev) platform_set_drvdata(pdev, srom); srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, - ARRAY_SIZE(exynos_srom_offsets)); + ARRAY_SIZE(exynos_srom_offsets)); if (!srom->reg_offset) { iounmap(srom->reg_base); return -ENOMEM; @@ -157,16 +157,16 @@ static int exynos_srom_probe(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static void exynos_srom_save(void __iomem *base, - struct exynos_srom_reg_dump *rd, - unsigned int num_regs) + struct exynos_srom_reg_dump *rd, + unsigned int num_regs) { for (; num_regs > 0; --num_regs, ++rd) rd->value = readl(base + rd->offset); } static void exynos_srom_restore(void __iomem *base, - const struct exynos_srom_reg_dump *rd, - unsigned int num_regs) + const struct exynos_srom_reg_dump *rd, + unsigned int num_regs) { for (; num_regs > 0; --num_regs, ++rd) writel(rd->value, base + rd->offset); @@ -177,7 +177,7 @@ static int exynos_srom_suspend(struct device *dev) struct exynos_srom *srom = dev_get_drvdata(dev); exynos_srom_save(srom->reg_base, srom->reg_offset, - ARRAY_SIZE(exynos_srom_offsets)); + ARRAY_SIZE(exynos_srom_offsets)); return 0; } @@ -186,7 +186,7 @@ static int exynos_srom_resume(struct device *dev) struct exynos_srom *srom = dev_get_drvdata(dev); exynos_srom_restore(srom->reg_base, srom->reg_offset, - ARRAY_SIZE(exynos_srom_offsets)); + ARRAY_SIZE(exynos_srom_offsets)); return 0; } #endif diff --git a/drivers/memory/samsung/exynos5422-dmc.c b/drivers/memory/samsung/exynos5422-dmc.c index 25196d6268e2..4312233e8e0a 100644 --- a/drivers/memory/samsung/exynos5422-dmc.c +++ b/drivers/memory/samsung/exynos5422-dmc.c @@ -270,12 +270,14 @@ static int find_target_freq_idx(struct exynos5_dmc *dmc, * This function switches between these banks according to the * currently used clock source. */ -static void exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set) +static int exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set) { unsigned int reg; int ret; ret = regmap_read(dmc->clk_regmap, CDREX_LPDDR3PHY_CON3, ®); + if (ret) + return ret; if (set) reg |= EXYNOS5_TIMING_SET_SWI; @@ -283,6 +285,8 @@ static void exynos5_switch_timing_regs(struct exynos5_dmc *dmc, bool set) reg &= ~EXYNOS5_TIMING_SET_SWI; regmap_write(dmc->clk_regmap, CDREX_LPDDR3PHY_CON3, reg); + + return 0; } /** @@ -516,7 +520,7 @@ exynos5_dmc_switch_to_bypass_configuration(struct exynos5_dmc *dmc, /* * Delays are long enough, so use them for the new coming clock. */ - exynos5_switch_timing_regs(dmc, USE_MX_MSPLL_TIMINGS); + ret = exynos5_switch_timing_regs(dmc, USE_MX_MSPLL_TIMINGS); return ret; } @@ -577,7 +581,9 @@ exynos5_dmc_change_freq_and_volt(struct exynos5_dmc *dmc, clk_set_rate(dmc->fout_bpll, target_rate); - exynos5_switch_timing_regs(dmc, USE_BPLL_TIMINGS); + ret = exynos5_switch_timing_regs(dmc, USE_BPLL_TIMINGS); + if (ret) + goto disable_clocks; ret = clk_set_parent(dmc->mout_mclk_cdrex, dmc->mout_bpll); if (ret) @@ -1392,7 +1398,7 @@ static int exynos5_dmc_probe(struct platform_device *pdev) return PTR_ERR(dmc->base_drexi1); dmc->clk_regmap = syscon_regmap_lookup_by_phandle(np, - "samsung,syscon-clk"); + "samsung,syscon-clk"); if (IS_ERR(dmc->clk_regmap)) return PTR_ERR(dmc->clk_regmap); @@ -1471,7 +1477,6 @@ static int exynos5_dmc_probe(struct platform_device *pdev) exynos5_dmc_df_profile.polling_ms = 500; } - dmc->df = devm_devfreq_add_device(dev, &exynos5_dmc_df_profile, DEVFREQ_GOV_SIMPLE_ONDEMAND, &dmc->gov_data); diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig index fbfbaada61a2..9f0a96bf9ccc 100644 --- a/drivers/memory/tegra/Kconfig +++ b/drivers/memory/tegra/Kconfig @@ -36,3 +36,17 @@ config TEGRA124_EMC Tegra124 chips. The EMC controls the external DRAM on the board. This driver is required to change memory timings / clock rate for external memory. + +config TEGRA210_EMC_TABLE + bool + depends on ARCH_TEGRA_210_SOC + +config TEGRA210_EMC + tristate "NVIDIA Tegra210 External Memory Controller driver" + depends on TEGRA_MC && ARCH_TEGRA_210_SOC + select TEGRA210_EMC_TABLE + help + This driver is for the External Memory Controller (EMC) found on + Tegra210 chips. The EMC controls the external DRAM on the board. + This driver is required to change memory timings / clock rate for + external memory. diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile index 529d10bc5650..6c1a2ecc6628 100644 --- a/drivers/memory/tegra/Makefile +++ b/drivers/memory/tegra/Makefile @@ -13,5 +13,9 @@ obj-$(CONFIG_TEGRA_MC) += tegra-mc.o obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o +obj-$(CONFIG_TEGRA210_EMC_TABLE) += tegra210-emc-table.o +obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o tegra186-emc.o obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra186-emc.o + +tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 957c6eb74ff9..afa3ba45c9e6 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -34,6 +34,7 @@ #define MC_EMEM_ARB_TIMING_W2W 0xbc #define MC_EMEM_ARB_TIMING_R2W 0xc0 #define MC_EMEM_ARB_TIMING_W2R 0xc4 +#define MC_EMEM_ARB_MISC2 0xc8 #define MC_EMEM_ARB_DA_TURNS 0xd0 #define MC_EMEM_ARB_DA_COVERS 0xd4 #define MC_EMEM_ARB_MISC0 0xd8 diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index 33b8216bac30..ba5cb1f4dfc2 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -984,6 +984,7 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, static const struct of_device_id tegra_emc_of_match[] = { { .compatible = "nvidia,tegra124-emc" }, + { .compatible = "nvidia,tegra132-emc" }, {} }; @@ -1178,11 +1179,11 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc) return; } - debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root, emc, + debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc, &tegra_emc_debug_available_rates_fops); - debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, + debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc, &tegra_emc_debug_min_rate_fops); - debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, + debugfs_create_file("max_rate", 0644, emc->debugfs.root, emc, &tegra_emc_debug_max_rate_fops); } diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c index 97f26bc77ad4..8478f59db432 100644 --- a/drivers/memory/tegra/tegra186-emc.c +++ b/drivers/memory/tegra/tegra186-emc.c @@ -185,7 +185,7 @@ static int tegra186_emc_probe(struct platform_device *pdev) if (IS_ERR(emc->clk)) { err = PTR_ERR(emc->clk); dev_err(&pdev->dev, "failed to get EMC clock: %d\n", err); - return err; + goto put_bpmp; } platform_set_drvdata(pdev, emc); @@ -201,7 +201,7 @@ static int tegra186_emc_probe(struct platform_device *pdev) err = tegra_bpmp_transfer(emc->bpmp, &msg); if (err < 0) { dev_err(&pdev->dev, "failed to EMC DVFS pairs: %d\n", err); - return err; + goto put_bpmp; } emc->debugfs.min_rate = ULONG_MAX; @@ -211,8 +211,10 @@ static int tegra186_emc_probe(struct platform_device *pdev) emc->dvfs = devm_kmalloc_array(&pdev->dev, emc->num_dvfs, sizeof(*emc->dvfs), GFP_KERNEL); - if (!emc->dvfs) - return -ENOMEM; + if (!emc->dvfs) { + err = -ENOMEM; + goto put_bpmp; + } dev_dbg(&pdev->dev, "%u DVFS pairs:\n", emc->num_dvfs); @@ -237,15 +239,10 @@ static int tegra186_emc_probe(struct platform_device *pdev) "failed to set rate range [%lu-%lu] for %pC\n", emc->debugfs.min_rate, emc->debugfs.max_rate, emc->clk); - return err; + goto put_bpmp; } emc->debugfs.root = debugfs_create_dir("emc", NULL); - if (!emc->debugfs.root) { - dev_err(&pdev->dev, "failed to create debugfs directory\n"); - return 0; - } - debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root, emc, &tegra186_emc_debug_available_rates_fops); debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, @@ -254,6 +251,10 @@ static int tegra186_emc_probe(struct platform_device *pdev) emc, &tegra186_emc_debug_max_rate_fops); return 0; + +put_bpmp: + tegra_bpmp_put(emc->bpmp); + return err; } static int tegra186_emc_remove(struct platform_device *pdev) @@ -267,10 +268,10 @@ static int tegra186_emc_remove(struct platform_device *pdev) } static const struct of_device_id tegra186_emc_of_match[] = { -#if defined(CONFIG_ARCH_TEGRA186_SOC) +#if defined(CONFIG_ARCH_TEGRA_186_SOC) { .compatible = "nvidia,tegra186-emc" }, #endif -#if defined(CONFIG_ARCH_TEGRA194_SOC) +#if defined(CONFIG_ARCH_TEGRA_194_SOC) { .compatible = "nvidia,tegra194-emc" }, #endif { /* sentinel */ } diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c index 5d53f11ca7b6..e25c954dde2e 100644 --- a/drivers/memory/tegra/tegra186.c +++ b/drivers/memory/tegra/tegra186.c @@ -1570,12 +1570,12 @@ static const struct of_device_id tegra186_mc_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra186_mc_of_match); -static int tegra186_mc_suspend(struct device *dev) +static int __maybe_unused tegra186_mc_suspend(struct device *dev) { return 0; } -static int tegra186_mc_resume(struct device *dev) +static int __maybe_unused tegra186_mc_resume(struct device *dev) { struct tegra186_mc *mc = dev_get_drvdata(dev); diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c index b16715e9515d..027f46287dbf 100644 --- a/drivers/memory/tegra/tegra20-emc.c +++ b/drivers/memory/tegra/tegra20-emc.c @@ -7,11 +7,11 @@ #include <linux/clk.h> #include <linux/clk/tegra.h> -#include <linux/completion.h> #include <linux/debugfs.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -144,7 +144,6 @@ struct emc_timing { struct tegra_emc { struct device *dev; - struct completion clk_handshake_complete; struct notifier_block clk_nb; struct clk *clk; void __iomem *regs; @@ -162,17 +161,13 @@ struct tegra_emc { static irqreturn_t tegra_emc_isr(int irq, void *data) { struct tegra_emc *emc = data; - u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; + u32 intmask = EMC_REFRESH_OVERFLOW_INT; u32 status; status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask; if (!status) return IRQ_NONE; - /* notify about EMC-CAR handshake completion */ - if (status & EMC_CLKCHANGE_COMPLETE_INT) - complete(&emc->clk_handshake_complete); - /* notify about HW problem */ if (status & EMC_REFRESH_OVERFLOW_INT) dev_err_ratelimited(emc->dev, @@ -224,14 +219,13 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate) /* wait until programming has settled */ readl_relaxed(emc->regs + emc_timing_registers[i - 1]); - reinit_completion(&emc->clk_handshake_complete); - return 0; } static int emc_complete_timing_change(struct tegra_emc *emc, bool flush) { - unsigned long timeout; + int err; + u32 v; dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush); @@ -242,11 +236,12 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush) return 0; } - timeout = wait_for_completion_timeout(&emc->clk_handshake_complete, - msecs_to_jiffies(100)); - if (timeout == 0) { - dev_err(emc->dev, "EMC-CAR handshake failed\n"); - return -EIO; + err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, v, + v & EMC_CLKCHANGE_COMPLETE_INT, + 1, 100); + if (err) { + dev_err(emc->dev, "emc-car handshake timeout: %d\n", err); + return err; } return 0; @@ -412,7 +407,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev) static int emc_setup_hw(struct tegra_emc *emc) { - u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; + u32 intmask = EMC_REFRESH_OVERFLOW_INT; u32 emc_cfg, emc_dbg; emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2); @@ -647,11 +642,11 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc) return; } - debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root, + debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc, &tegra_emc_debug_available_rates_fops); - debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, + debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc, &tegra_emc_debug_min_rate_fops); - debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, + debugfs_create_file("max_rate", 0644, emc->debugfs.root, emc, &tegra_emc_debug_max_rate_fops); } @@ -686,7 +681,6 @@ static int tegra_emc_probe(struct platform_device *pdev) return -ENOMEM; } - init_completion(&emc->clk_handshake_complete); emc->clk_nb.notifier_call = tegra_emc_clk_change_notify; emc->dev = &pdev->dev; diff --git a/drivers/memory/tegra/tegra210-emc-cc-r21021.c b/drivers/memory/tegra/tegra210-emc-cc-r21021.c new file mode 100644 index 000000000000..ff55a17896fa --- /dev/null +++ b/drivers/memory/tegra/tegra210-emc-cc-r21021.c @@ -0,0 +1,1775 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/of.h> + +#include <soc/tegra/mc.h> + +#include "tegra210-emc.h" +#include "tegra210-mc.h" + +/* + * Enable flags for specifying verbosity. + */ +#define INFO (1 << 0) +#define STEPS (1 << 1) +#define SUB_STEPS (1 << 2) +#define PRELOCK (1 << 3) +#define PRELOCK_STEPS (1 << 4) +#define ACTIVE_EN (1 << 5) +#define PRAMP_UP (1 << 6) +#define PRAMP_DN (1 << 7) +#define EMA_WRITES (1 << 10) +#define EMA_UPDATES (1 << 11) +#define PER_TRAIN (1 << 16) +#define CC_PRINT (1 << 17) +#define CCFIFO (1 << 29) +#define REGS (1 << 30) +#define REG_LISTS (1 << 31) + +#define emc_dbg(emc, flags, ...) dev_dbg(emc->dev, __VA_ARGS__) + +#define DVFS_CLOCK_CHANGE_VERSION 21021 +#define EMC_PRELOCK_VERSION 2101 + +enum { + DVFS_SEQUENCE = 1, + WRITE_TRAINING_SEQUENCE = 2, + PERIODIC_TRAINING_SEQUENCE = 3, + DVFS_PT1 = 10, + DVFS_UPDATE = 11, + TRAINING_PT1 = 12, + TRAINING_UPDATE = 13, + PERIODIC_TRAINING_UPDATE = 14 +}; + +/* + * PTFV defines - basically just indexes into the per table PTFV array. + */ +#define PTFV_DQSOSC_MOVAVG_C0D0U0_INDEX 0 +#define PTFV_DQSOSC_MOVAVG_C0D0U1_INDEX 1 +#define PTFV_DQSOSC_MOVAVG_C0D1U0_INDEX 2 +#define PTFV_DQSOSC_MOVAVG_C0D1U1_INDEX 3 +#define PTFV_DQSOSC_MOVAVG_C1D0U0_INDEX 4 +#define PTFV_DQSOSC_MOVAVG_C1D0U1_INDEX 5 +#define PTFV_DQSOSC_MOVAVG_C1D1U0_INDEX 6 +#define PTFV_DQSOSC_MOVAVG_C1D1U1_INDEX 7 +#define PTFV_DVFS_SAMPLES_INDEX 9 +#define PTFV_MOVAVG_WEIGHT_INDEX 10 +#define PTFV_CONFIG_CTRL_INDEX 11 + +#define PTFV_CONFIG_CTRL_USE_PREVIOUS_EMA (1 << 0) + +/* + * Do arithmetic in fixed point. + */ +#define MOVAVG_PRECISION_FACTOR 100 + +/* + * The division portion of the average operation. + */ +#define __AVERAGE_PTFV(dev) \ + ({ next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] = \ + next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \ + next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; }) + +/* + * Convert val to fixed point and add it to the temporary average. + */ +#define __INCREMENT_PTFV(dev, val) \ + ({ next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] += \ + ((val) * MOVAVG_PRECISION_FACTOR); }) + +/* + * Convert a moving average back to integral form and return the value. + */ +#define __MOVAVG_AC(timing, dev) \ + ((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \ + MOVAVG_PRECISION_FACTOR) + +/* Weighted update. */ +#define __WEIGHTED_UPDATE_PTFV(dev, nval) \ + do { \ + int w = PTFV_MOVAVG_WEIGHT_INDEX; \ + int dqs = PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX; \ + \ + next->ptfv_list[dqs] = \ + ((nval * MOVAVG_PRECISION_FACTOR) + \ + (next->ptfv_list[dqs] * \ + next->ptfv_list[w])) / \ + (next->ptfv_list[w] + 1); \ + \ + emc_dbg(emc, EMA_UPDATES, "%s: (s=%lu) EMA: %u\n", \ + __stringify(dev), nval, next->ptfv_list[dqs]); \ + } while (0) + +/* Access a particular average. */ +#define __MOVAVG(timing, dev) \ + ((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX]) + +static u32 update_clock_tree_delay(struct tegra210_emc *emc, int type) +{ + bool periodic_training_update = type == PERIODIC_TRAINING_UPDATE; + struct tegra210_emc_timing *last = emc->last; + struct tegra210_emc_timing *next = emc->next; + u32 last_timing_rate_mhz = last->rate / 1000; + u32 next_timing_rate_mhz = next->rate / 1000; + bool dvfs_update = type == DVFS_UPDATE; + s32 tdel = 0, tmdel = 0, adel = 0; + bool dvfs_pt1 = type == DVFS_PT1; + unsigned long cval = 0; + u32 temp[2][2], value; + unsigned int i; + + /* + * Dev0 MSB. + */ + if (dvfs_pt1 || periodic_training_update) { + value = tegra210_emc_mrr_read(emc, 2, 19); + + for (i = 0; i < emc->num_channels; i++) { + temp[i][0] = (value & 0x00ff) << 8; + temp[i][1] = (value & 0xff00) << 0; + value >>= 16; + } + + /* + * Dev0 LSB. + */ + value = tegra210_emc_mrr_read(emc, 2, 18); + + for (i = 0; i < emc->num_channels; i++) { + temp[i][0] |= (value & 0x00ff) >> 0; + temp[i][1] |= (value & 0xff00) >> 8; + value >>= 16; + } + } + + if (dvfs_pt1 || periodic_training_update) { + cval = tegra210_emc_actual_osc_clocks(last->run_clocks); + cval *= 1000000; + cval /= last_timing_rate_mhz * 2 * temp[0][0]; + } + + if (dvfs_pt1) + __INCREMENT_PTFV(C0D0U0, cval); + else if (dvfs_update) + __AVERAGE_PTFV(C0D0U0); + else if (periodic_training_update) + __WEIGHTED_UPDATE_PTFV(C0D0U0, cval); + + if (dvfs_update || periodic_training_update) { + tdel = next->current_dram_clktree[C0D0U0] - + __MOVAVG_AC(next, C0D0U0); + tmdel = (tdel < 0) ? -1 * tdel : tdel; + adel = tmdel; + + if (tmdel * 128 * next_timing_rate_mhz / 1000000 > + next->tree_margin) + next->current_dram_clktree[C0D0U0] = + __MOVAVG_AC(next, C0D0U0); + } + + if (dvfs_pt1 || periodic_training_update) { + cval = tegra210_emc_actual_osc_clocks(last->run_clocks); + cval *= 1000000; + cval /= last_timing_rate_mhz * 2 * temp[0][1]; + } + + if (dvfs_pt1) + __INCREMENT_PTFV(C0D0U1, cval); + else if (dvfs_update) + __AVERAGE_PTFV(C0D0U1); + else if (periodic_training_update) + __WEIGHTED_UPDATE_PTFV(C0D0U1, cval); + + if (dvfs_update || periodic_training_update) { + tdel = next->current_dram_clktree[C0D0U1] - + __MOVAVG_AC(next, C0D0U1); + tmdel = (tdel < 0) ? -1 * tdel : tdel; + + if (tmdel > adel) + adel = tmdel; + + if (tmdel * 128 * next_timing_rate_mhz / 1000000 > + next->tree_margin) + next->current_dram_clktree[C0D0U1] = + __MOVAVG_AC(next, C0D0U1); + } + + if (emc->num_channels > 1) { + if (dvfs_pt1 || periodic_training_update) { + cval = tegra210_emc_actual_osc_clocks(last->run_clocks); + cval *= 1000000; + cval /= last_timing_rate_mhz * 2 * temp[1][0]; + } + + if (dvfs_pt1) + __INCREMENT_PTFV(C1D0U0, cval); + else if (dvfs_update) + __AVERAGE_PTFV(C1D0U0); + else if (periodic_training_update) + __WEIGHTED_UPDATE_PTFV(C1D0U0, cval); + + if (dvfs_update || periodic_training_update) { + tdel = next->current_dram_clktree[C1D0U0] - + __MOVAVG_AC(next, C1D0U0); + tmdel = (tdel < 0) ? -1 * tdel : tdel; + + if (tmdel > adel) + adel = tmdel; + + if (tmdel * 128 * next_timing_rate_mhz / 1000000 > + next->tree_margin) + next->current_dram_clktree[C1D0U0] = + __MOVAVG_AC(next, C1D0U0); + } + + if (dvfs_pt1 || periodic_training_update) { + cval = tegra210_emc_actual_osc_clocks(last->run_clocks); + cval *= 1000000; + cval /= last_timing_rate_mhz * 2 * temp[1][1]; + } + + if (dvfs_pt1) + __INCREMENT_PTFV(C1D0U1, cval); + else if (dvfs_update) + __AVERAGE_PTFV(C1D0U1); + else if (periodic_training_update) + __WEIGHTED_UPDATE_PTFV(C1D0U1, cval); + + if (dvfs_update || periodic_training_update) { + tdel = next->current_dram_clktree[C1D0U1] - + __MOVAVG_AC(next, C1D0U1); + tmdel = (tdel < 0) ? -1 * tdel : tdel; + + if (tmdel > adel) + adel = tmdel; + + if (tmdel * 128 * next_timing_rate_mhz / 1000000 > + next->tree_margin) + next->current_dram_clktree[C1D0U1] = + __MOVAVG_AC(next, C1D0U1); + } + } + + if (emc->num_devices < 2) + goto done; + + /* + * Dev1 MSB. + */ + if (dvfs_pt1 || periodic_training_update) { + value = tegra210_emc_mrr_read(emc, 1, 19); + + for (i = 0; i < emc->num_channels; i++) { + temp[i][0] = (value & 0x00ff) << 8; + temp[i][1] = (value & 0xff00) << 0; + value >>= 16; + } + + /* + * Dev1 LSB. + */ + value = tegra210_emc_mrr_read(emc, 2, 18); + + for (i = 0; i < emc->num_channels; i++) { + temp[i][0] |= (value & 0x00ff) >> 0; + temp[i][1] |= (value & 0xff00) >> 8; + value >>= 16; + } + } + + if (dvfs_pt1 || periodic_training_update) { + cval = tegra210_emc_actual_osc_clocks(last->run_clocks); + cval *= 1000000; + cval /= last_timing_rate_mhz * 2 * temp[0][0]; + } + + if (dvfs_pt1) + __INCREMENT_PTFV(C0D1U0, cval); + else if (dvfs_update) + __AVERAGE_PTFV(C0D1U0); + else if (periodic_training_update) + __WEIGHTED_UPDATE_PTFV(C0D1U0, cval); + + if (dvfs_update || periodic_training_update) { + tdel = next->current_dram_clktree[C0D1U0] - + __MOVAVG_AC(next, C0D1U0); + tmdel = (tdel < 0) ? -1 * tdel : tdel; + + if (tmdel > adel) + adel = tmdel; + + if (tmdel * 128 * next_timing_rate_mhz / 1000000 > + next->tree_margin) + next->current_dram_clktree[C0D1U0] = + __MOVAVG_AC(next, C0D1U0); + } + + if (dvfs_pt1 || periodic_training_update) { + cval = tegra210_emc_actual_osc_clocks(last->run_clocks); + cval *= 1000000; + cval /= last_timing_rate_mhz * 2 * temp[0][1]; + } + + if (dvfs_pt1) + __INCREMENT_PTFV(C0D1U1, cval); + else if (dvfs_update) + __AVERAGE_PTFV(C0D1U1); + else if (periodic_training_update) + __WEIGHTED_UPDATE_PTFV(C0D1U1, cval); + + if (dvfs_update || periodic_training_update) { + tdel = next->current_dram_clktree[C0D1U1] - + __MOVAVG_AC(next, C0D1U1); + tmdel = (tdel < 0) ? -1 * tdel : tdel; + + if (tmdel > adel) + adel = tmdel; + + if (tmdel * 128 * next_timing_rate_mhz / 1000000 > + next->tree_margin) + next->current_dram_clktree[C0D1U1] = + __MOVAVG_AC(next, C0D1U1); + } + + if (emc->num_channels > 1) { + if (dvfs_pt1 || periodic_training_update) { + cval = tegra210_emc_actual_osc_clocks(last->run_clocks); + cval *= 1000000; + cval /= last_timing_rate_mhz * 2 * temp[1][0]; + } + + if (dvfs_pt1) + __INCREMENT_PTFV(C1D1U0, cval); + else if (dvfs_update) + __AVERAGE_PTFV(C1D1U0); + else if (periodic_training_update) + __WEIGHTED_UPDATE_PTFV(C1D1U0, cval); + + if (dvfs_update || periodic_training_update) { + tdel = next->current_dram_clktree[C1D1U0] - + __MOVAVG_AC(next, C1D1U0); + tmdel = (tdel < 0) ? -1 * tdel : tdel; + + if (tmdel > adel) + adel = tmdel; + + if (tmdel * 128 * next_timing_rate_mhz / 1000000 > + next->tree_margin) + next->current_dram_clktree[C1D1U0] = + __MOVAVG_AC(next, C1D1U0); + } + + if (dvfs_pt1 || periodic_training_update) { + cval = tegra210_emc_actual_osc_clocks(last->run_clocks); + cval *= 1000000; + cval /= last_timing_rate_mhz * 2 * temp[1][1]; + } + + if (dvfs_pt1) + __INCREMENT_PTFV(C1D1U1, cval); + else if (dvfs_update) + __AVERAGE_PTFV(C1D1U1); + else if (periodic_training_update) + __WEIGHTED_UPDATE_PTFV(C1D1U1, cval); + + if (dvfs_update || periodic_training_update) { + tdel = next->current_dram_clktree[C1D1U1] - + __MOVAVG_AC(next, C1D1U1); + tmdel = (tdel < 0) ? -1 * tdel : tdel; + + if (tmdel > adel) + adel = tmdel; + + if (tmdel * 128 * next_timing_rate_mhz / 1000000 > + next->tree_margin) + next->current_dram_clktree[C1D1U1] = + __MOVAVG_AC(next, C1D1U1); + } + } + +done: + return adel; +} + +static u32 periodic_compensation_handler(struct tegra210_emc *emc, u32 type, + struct tegra210_emc_timing *last, + struct tegra210_emc_timing *next) +{ +#define __COPY_EMA(nt, lt, dev) \ + ({ __MOVAVG(nt, dev) = __MOVAVG(lt, dev) * \ + (nt)->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; }) + + u32 i, adel = 0, samples = next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; + u32 delay; + + delay = tegra210_emc_actual_osc_clocks(last->run_clocks); + delay *= 1000; + delay = 2 + (delay / last->rate); + + if (!next->periodic_training) + return 0; + + if (type == DVFS_SEQUENCE) { + if (last->periodic_training && + (next->ptfv_list[PTFV_CONFIG_CTRL_INDEX] & + PTFV_CONFIG_CTRL_USE_PREVIOUS_EMA)) { + /* + * If the previous frequency was using periodic + * calibration then we can reuse the previous + * frequencies EMA data. + */ + __COPY_EMA(next, last, C0D0U0); + __COPY_EMA(next, last, C0D0U1); + __COPY_EMA(next, last, C1D0U0); + __COPY_EMA(next, last, C1D0U1); + __COPY_EMA(next, last, C0D1U0); + __COPY_EMA(next, last, C0D1U1); + __COPY_EMA(next, last, C1D1U0); + __COPY_EMA(next, last, C1D1U1); + } else { + /* Reset the EMA.*/ + __MOVAVG(next, C0D0U0) = 0; + __MOVAVG(next, C0D0U1) = 0; + __MOVAVG(next, C1D0U0) = 0; + __MOVAVG(next, C1D0U1) = 0; + __MOVAVG(next, C0D1U0) = 0; + __MOVAVG(next, C0D1U1) = 0; + __MOVAVG(next, C1D1U0) = 0; + __MOVAVG(next, C1D1U1) = 0; + + for (i = 0; i < samples; i++) { + tegra210_emc_start_periodic_compensation(emc); + udelay(delay); + + /* + * Generate next sample of data. + */ + adel = update_clock_tree_delay(emc, DVFS_PT1); + } + } + + /* + * Seems like it should be part of the + * 'if (last_timing->periodic_training)' conditional + * since is already done for the else clause. + */ + adel = update_clock_tree_delay(emc, DVFS_UPDATE); + } + + if (type == PERIODIC_TRAINING_SEQUENCE) { + tegra210_emc_start_periodic_compensation(emc); + udelay(delay); + + adel = update_clock_tree_delay(emc, PERIODIC_TRAINING_UPDATE); + } + + return adel; +} + +static u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc) +{ + u32 emc_cfg, emc_cfg_o, emc_cfg_update, del, value; + u32 list[] = { + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3, + EMC_DATA_BRLSHFT_0, + EMC_DATA_BRLSHFT_1 + }; + struct tegra210_emc_timing *last = emc->last; + unsigned int items = ARRAY_SIZE(list), i; + unsigned long delay; + + if (last->periodic_training) { + emc_dbg(emc, PER_TRAIN, "Periodic training starting\n"); + + value = emc_readl(emc, EMC_DBG); + emc_cfg_o = emc_readl(emc, EMC_CFG); + emc_cfg = emc_cfg_o & ~(EMC_CFG_DYN_SELF_REF | + EMC_CFG_DRAM_ACPD | + EMC_CFG_DRAM_CLKSTOP_PD | + EMC_CFG_DRAM_CLKSTOP_PD); + + + /* + * 1. Power optimizations should be off. + */ + emc_writel(emc, emc_cfg, EMC_CFG); + + /* Does emc_timing_update() for above changes. */ + tegra210_emc_dll_disable(emc); + + for (i = 0; i < emc->num_channels; i++) + tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, + EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, + 0); + + for (i = 0; i < emc->num_channels; i++) + tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, + EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, + 0); + + emc_cfg_update = value = emc_readl(emc, EMC_CFG_UPDATE); + value &= ~EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_MASK; + value |= (2 << EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_SHIFT); + emc_writel(emc, value, EMC_CFG_UPDATE); + + /* + * 2. osc kick off - this assumes training and dvfs have set + * correct MR23. + */ + tegra210_emc_start_periodic_compensation(emc); + + /* + * 3. Let dram capture its clock tree delays. + */ + delay = tegra210_emc_actual_osc_clocks(last->run_clocks); + delay *= 1000; + delay /= last->rate + 1; + udelay(delay); + + /* + * 4. Check delta wrt previous values (save value if margin + * exceeds what is set in table). + */ + del = periodic_compensation_handler(emc, + PERIODIC_TRAINING_SEQUENCE, + last, last); + + /* + * 5. Apply compensation w.r.t. trained values (if clock tree + * has drifted more than the set margin). + */ + if (last->tree_margin < ((del * 128 * (last->rate / 1000)) / 1000000)) { + for (i = 0; i < items; i++) { + value = tegra210_emc_compensate(last, list[i]); + emc_dbg(emc, EMA_WRITES, "0x%08x <= 0x%08x\n", + list[i], value); + emc_writel(emc, value, list[i]); + } + } + + emc_writel(emc, emc_cfg_o, EMC_CFG); + + /* + * 6. Timing update actally applies the new trimmers. + */ + tegra210_emc_timing_update(emc); + + /* 6.1. Restore the UPDATE_DLL_IN_UPDATE field. */ + emc_writel(emc, emc_cfg_update, EMC_CFG_UPDATE); + + /* 6.2. Restore the DLL. */ + tegra210_emc_dll_enable(emc); + } + + return 0; +} + +/* + * Do the clock change sequence. + */ +static void tegra210_emc_r21021_set_clock(struct tegra210_emc *emc, u32 clksrc) +{ + /* state variables */ + static bool fsp_for_next_freq; + /* constant configuration parameters */ + const bool save_restore_clkstop_pd = true; + const u32 zqcal_before_cc_cutoff = 2400; + const bool cya_allow_ref_cc = false; + const bool cya_issue_pc_ref = false; + const bool opt_cc_short_zcal = true; + const bool ref_b4_sref_en = false; + const u32 tZQCAL_lpddr4 = 1000000; + const bool opt_short_zcal = true; + const bool opt_do_sw_qrst = true; + const u32 opt_dvfs_mode = MAN_SR; + /* + * This is the timing table for the source frequency. It does _not_ + * necessarily correspond to the actual timing values in the EMC at the + * moment. If the boot BCT differs from the table then this can happen. + * However, we need it for accessing the dram_timings (which are not + * really registers) array for the current frequency. + */ + struct tegra210_emc_timing *fake, *last = emc->last, *next = emc->next; + u32 tRTM, RP_war, R2P_war, TRPab_war, deltaTWATM, W2P_war, tRPST; + u32 mr13_flip_fspwr, mr13_flip_fspop, ramp_up_wait, ramp_down_wait; + u32 zq_wait_long, zq_latch_dvfs_wait_time, tZQCAL_lpddr4_fc_adj; + u32 emc_auto_cal_config, auto_cal_en, emc_cfg, emc_sel_dpd_ctrl; + u32 tFC_lpddr4 = 1000 * next->dram_timings[T_FC_LPDDR4]; + u32 bg_reg_mode_change, enable_bglp_reg, enable_bg_reg; + bool opt_zcal_en_cc = false, is_lpddr3 = false; + bool compensate_trimmer_applicable = false; + u32 emc_dbg, emc_cfg_pipe_clk, emc_pin; + u32 src_clk_period, dst_clk_period; /* in picoseconds */ + bool shared_zq_resistor = false; + u32 value, dram_type; + u32 opt_dll_mode = 0; + unsigned long delay; + unsigned int i; + + emc_dbg(emc, INFO, "Running clock change.\n"); + + /* XXX fake == last */ + fake = tegra210_emc_find_timing(emc, last->rate * 1000UL); + fsp_for_next_freq = !fsp_for_next_freq; + + value = emc_readl(emc, EMC_FBIO_CFG5) & EMC_FBIO_CFG5_DRAM_TYPE_MASK; + dram_type = value >> EMC_FBIO_CFG5_DRAM_TYPE_SHIFT; + + if (last->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX] & BIT(31)) + shared_zq_resistor = true; + + if ((next->burst_regs[EMC_ZCAL_INTERVAL_INDEX] != 0 && + last->burst_regs[EMC_ZCAL_INTERVAL_INDEX] == 0) || + dram_type == DRAM_TYPE_LPDDR4) + opt_zcal_en_cc = true; + + if (dram_type == DRAM_TYPE_DDR3) + opt_dll_mode = tegra210_emc_get_dll_state(next); + + if ((next->burst_regs[EMC_FBIO_CFG5_INDEX] & BIT(25)) && + (dram_type == DRAM_TYPE_LPDDR2)) + is_lpddr3 = true; + + emc_readl(emc, EMC_CFG); + emc_readl(emc, EMC_AUTO_CAL_CONFIG); + + src_clk_period = 1000000000 / last->rate; + dst_clk_period = 1000000000 / next->rate; + + if (dst_clk_period <= zqcal_before_cc_cutoff) + tZQCAL_lpddr4_fc_adj = tZQCAL_lpddr4 - tFC_lpddr4; + else + tZQCAL_lpddr4_fc_adj = tZQCAL_lpddr4; + + tZQCAL_lpddr4_fc_adj /= dst_clk_period; + + emc_dbg = emc_readl(emc, EMC_DBG); + emc_pin = emc_readl(emc, EMC_PIN); + emc_cfg_pipe_clk = emc_readl(emc, EMC_CFG_PIPE_CLK); + + emc_cfg = next->burst_regs[EMC_CFG_INDEX]; + emc_cfg &= ~(EMC_CFG_DYN_SELF_REF | EMC_CFG_DRAM_ACPD | + EMC_CFG_DRAM_CLKSTOP_SR | EMC_CFG_DRAM_CLKSTOP_PD); + emc_sel_dpd_ctrl = next->emc_sel_dpd_ctrl; + emc_sel_dpd_ctrl &= ~(EMC_SEL_DPD_CTRL_CLK_SEL_DPD_EN | + EMC_SEL_DPD_CTRL_CA_SEL_DPD_EN | + EMC_SEL_DPD_CTRL_RESET_SEL_DPD_EN | + EMC_SEL_DPD_CTRL_ODT_SEL_DPD_EN | + EMC_SEL_DPD_CTRL_DATA_SEL_DPD_EN); + + emc_dbg(emc, INFO, "Clock change version: %d\n", + DVFS_CLOCK_CHANGE_VERSION); + emc_dbg(emc, INFO, "DRAM type = %d\n", dram_type); + emc_dbg(emc, INFO, "DRAM dev #: %u\n", emc->num_devices); + emc_dbg(emc, INFO, "Next EMC clksrc: 0x%08x\n", clksrc); + emc_dbg(emc, INFO, "DLL clksrc: 0x%08x\n", next->dll_clk_src); + emc_dbg(emc, INFO, "last rate: %u, next rate %u\n", last->rate, + next->rate); + emc_dbg(emc, INFO, "last period: %u, next period: %u\n", + src_clk_period, dst_clk_period); + emc_dbg(emc, INFO, " shared_zq_resistor: %d\n", !!shared_zq_resistor); + emc_dbg(emc, INFO, " num_channels: %u\n", emc->num_channels); + emc_dbg(emc, INFO, " opt_dll_mode: %d\n", opt_dll_mode); + + /* + * Step 1: + * Pre DVFS SW sequence. + */ + emc_dbg(emc, STEPS, "Step 1\n"); + emc_dbg(emc, STEPS, "Step 1.1: Disable DLL temporarily.\n"); + + value = emc_readl(emc, EMC_CFG_DIG_DLL); + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN; + emc_writel(emc, value, EMC_CFG_DIG_DLL); + + tegra210_emc_timing_update(emc); + + for (i = 0; i < emc->num_channels; i++) + tegra210_emc_wait_for_update(emc, i, EMC_CFG_DIG_DLL, + EMC_CFG_DIG_DLL_CFG_DLL_EN, 0); + + emc_dbg(emc, STEPS, "Step 1.2: Disable AUTOCAL temporarily.\n"); + + emc_auto_cal_config = next->emc_auto_cal_config; + auto_cal_en = emc_auto_cal_config & EMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE; + emc_auto_cal_config &= ~EMC_AUTO_CAL_CONFIG_AUTO_CAL_START; + emc_auto_cal_config |= EMC_AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL; + emc_auto_cal_config |= EMC_AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL; + emc_auto_cal_config |= auto_cal_en; + emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG); + emc_readl(emc, EMC_AUTO_CAL_CONFIG); /* Flush write. */ + + emc_dbg(emc, STEPS, "Step 1.3: Disable other power features.\n"); + + tegra210_emc_set_shadow_bypass(emc, ACTIVE); + emc_writel(emc, emc_cfg, EMC_CFG); + emc_writel(emc, emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL); + tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); + + if (next->periodic_training) { + tegra210_emc_reset_dram_clktree_values(next); + + for (i = 0; i < emc->num_channels; i++) + tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, + EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, + 0); + + for (i = 0; i < emc->num_channels; i++) + tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, + EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, + 0); + + tegra210_emc_start_periodic_compensation(emc); + + delay = 1000 * tegra210_emc_actual_osc_clocks(last->run_clocks); + udelay((delay / last->rate) + 2); + + value = periodic_compensation_handler(emc, DVFS_SEQUENCE, fake, + next); + value = (value * 128 * next->rate / 1000) / 1000000; + + if (next->periodic_training && value > next->tree_margin) + compensate_trimmer_applicable = true; + } + + emc_writel(emc, EMC_INTSTATUS_CLKCHANGE_COMPLETE, EMC_INTSTATUS); + tegra210_emc_set_shadow_bypass(emc, ACTIVE); + emc_writel(emc, emc_cfg, EMC_CFG); + emc_writel(emc, emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL); + emc_writel(emc, emc_cfg_pipe_clk | EMC_CFG_PIPE_CLK_CLK_ALWAYS_ON, + EMC_CFG_PIPE_CLK); + emc_writel(emc, next->emc_fdpd_ctrl_cmd_no_ramp & + ~EMC_FDPD_CTRL_CMD_NO_RAMP_CMD_DPD_NO_RAMP_ENABLE, + EMC_FDPD_CTRL_CMD_NO_RAMP); + + bg_reg_mode_change = + ((next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD) ^ + (last->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD)) || + ((next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD) ^ + (last->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD)); + enable_bglp_reg = + (next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD) == 0; + enable_bg_reg = + (next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD) == 0; + + if (bg_reg_mode_change) { + if (enable_bg_reg) + emc_writel(emc, last->burst_regs + [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + ~EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD, + EMC_PMACRO_BG_BIAS_CTRL_0); + + if (enable_bglp_reg) + emc_writel(emc, last->burst_regs + [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + ~EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD, + EMC_PMACRO_BG_BIAS_CTRL_0); + } + + /* Check if we need to turn on VREF generator. */ + if ((((last->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] & + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF) == 0) && + ((next->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] & + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF) == 1)) || + (((last->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] & + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) == 0) && + ((next->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] & + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) != 0))) { + u32 pad_tx_ctrl = + next->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX]; + u32 last_pad_tx_ctrl = + last->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX]; + u32 next_dq_e_ivref, next_dqs_e_ivref; + + next_dqs_e_ivref = pad_tx_ctrl & + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF; + next_dq_e_ivref = pad_tx_ctrl & + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF; + value = (last_pad_tx_ctrl & + ~EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF & + ~EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) | + next_dq_e_ivref | next_dqs_e_ivref; + emc_writel(emc, value, EMC_PMACRO_DATA_PAD_TX_CTRL); + udelay(1); + } else if (bg_reg_mode_change) { + udelay(1); + } + + tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); + + /* + * Step 2: + * Prelock the DLL. + */ + emc_dbg(emc, STEPS, "Step 2\n"); + + if (next->burst_regs[EMC_CFG_DIG_DLL_INDEX] & + EMC_CFG_DIG_DLL_CFG_DLL_EN) { + emc_dbg(emc, INFO, "Prelock enabled for target frequency.\n"); + value = tegra210_emc_dll_prelock(emc, clksrc); + emc_dbg(emc, INFO, "DLL out: 0x%03x\n", value); + } else { + emc_dbg(emc, INFO, "Disabling DLL for target frequency.\n"); + tegra210_emc_dll_disable(emc); + } + + /* + * Step 3: + * Prepare autocal for the clock change. + */ + emc_dbg(emc, STEPS, "Step 3\n"); + + tegra210_emc_set_shadow_bypass(emc, ACTIVE); + emc_writel(emc, next->emc_auto_cal_config2, EMC_AUTO_CAL_CONFIG2); + emc_writel(emc, next->emc_auto_cal_config3, EMC_AUTO_CAL_CONFIG3); + emc_writel(emc, next->emc_auto_cal_config4, EMC_AUTO_CAL_CONFIG4); + emc_writel(emc, next->emc_auto_cal_config5, EMC_AUTO_CAL_CONFIG5); + emc_writel(emc, next->emc_auto_cal_config6, EMC_AUTO_CAL_CONFIG6); + emc_writel(emc, next->emc_auto_cal_config7, EMC_AUTO_CAL_CONFIG7); + emc_writel(emc, next->emc_auto_cal_config8, EMC_AUTO_CAL_CONFIG8); + tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); + + emc_auto_cal_config |= (EMC_AUTO_CAL_CONFIG_AUTO_CAL_COMPUTE_START | + auto_cal_en); + emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG); + + /* + * Step 4: + * Update EMC_CFG. (??) + */ + emc_dbg(emc, STEPS, "Step 4\n"); + + if (src_clk_period > 50000 && dram_type == DRAM_TYPE_LPDDR4) + ccfifo_writel(emc, 1, EMC_SELF_REF, 0); + else + emc_writel(emc, next->emc_cfg_2, EMC_CFG_2); + + /* + * Step 5: + * Prepare reference variables for ZQCAL regs. + */ + emc_dbg(emc, STEPS, "Step 5\n"); + + if (dram_type == DRAM_TYPE_LPDDR4) + zq_wait_long = max((u32)1, div_o3(1000000, dst_clk_period)); + else if (dram_type == DRAM_TYPE_LPDDR2 || is_lpddr3) + zq_wait_long = max(next->min_mrs_wait, + div_o3(360000, dst_clk_period)) + 4; + else if (dram_type == DRAM_TYPE_DDR3) + zq_wait_long = max((u32)256, + div_o3(320000, dst_clk_period) + 2); + else + zq_wait_long = 0; + + /* + * Step 6: + * Training code - removed. + */ + emc_dbg(emc, STEPS, "Step 6\n"); + + /* + * Step 7: + * Program FSP reference registers and send MRWs to new FSPWR. + */ + emc_dbg(emc, STEPS, "Step 7\n"); + emc_dbg(emc, SUB_STEPS, "Step 7.1: Bug 200024907 - Patch RP R2P"); + + /* WAR 200024907 */ + if (dram_type == DRAM_TYPE_LPDDR4) { + u32 nRTP = 16; + + if (src_clk_period >= 1000000 / 1866) /* 535.91 ps */ + nRTP = 14; + + if (src_clk_period >= 1000000 / 1600) /* 625.00 ps */ + nRTP = 12; + + if (src_clk_period >= 1000000 / 1333) /* 750.19 ps */ + nRTP = 10; + + if (src_clk_period >= 1000000 / 1066) /* 938.09 ps */ + nRTP = 8; + + deltaTWATM = max_t(u32, div_o3(7500, src_clk_period), 8); + + /* + * Originally there was a + .5 in the tRPST calculation. + * However since we can't do FP in the kernel and the tRTM + * computation was in a floating point ceiling function, adding + * one to tRTP should be ok. There is no other source of non + * integer values, so the result was always going to be + * something for the form: f_ceil(N + .5) = N + 1; + */ + tRPST = (last->emc_mrw & 0x80) >> 7; + tRTM = fake->dram_timings[RL] + div_o3(3600, src_clk_period) + + max_t(u32, div_o3(7500, src_clk_period), 8) + tRPST + + 1 + nRTP; + + emc_dbg(emc, INFO, "tRTM = %u, EMC_RP = %u\n", tRTM, + next->burst_regs[EMC_RP_INDEX]); + + if (last->burst_regs[EMC_RP_INDEX] < tRTM) { + if (tRTM > (last->burst_regs[EMC_R2P_INDEX] + + last->burst_regs[EMC_RP_INDEX])) { + R2P_war = tRTM - last->burst_regs[EMC_RP_INDEX]; + RP_war = last->burst_regs[EMC_RP_INDEX]; + TRPab_war = last->burst_regs[EMC_TRPAB_INDEX]; + + if (R2P_war > 63) { + RP_war = R2P_war + + last->burst_regs[EMC_RP_INDEX] - 63; + + if (TRPab_war < RP_war) + TRPab_war = RP_war; + + R2P_war = 63; + } + } else { + R2P_war = last->burst_regs[EMC_R2P_INDEX]; + RP_war = last->burst_regs[EMC_RP_INDEX]; + TRPab_war = last->burst_regs[EMC_TRPAB_INDEX]; + } + + if (RP_war < deltaTWATM) { + W2P_war = last->burst_regs[EMC_W2P_INDEX] + + deltaTWATM - RP_war; + if (W2P_war > 63) { + RP_war = RP_war + W2P_war - 63; + if (TRPab_war < RP_war) + TRPab_war = RP_war; + W2P_war = 63; + } + } else { + W2P_war = last->burst_regs[ + EMC_W2P_INDEX]; + } + + if ((last->burst_regs[EMC_W2P_INDEX] ^ W2P_war) || + (last->burst_regs[EMC_R2P_INDEX] ^ R2P_war) || + (last->burst_regs[EMC_RP_INDEX] ^ RP_war) || + (last->burst_regs[EMC_TRPAB_INDEX] ^ TRPab_war)) { + emc_writel(emc, RP_war, EMC_RP); + emc_writel(emc, R2P_war, EMC_R2P); + emc_writel(emc, W2P_war, EMC_W2P); + emc_writel(emc, TRPab_war, EMC_TRPAB); + } + + tegra210_emc_timing_update(emc); + } else { + emc_dbg(emc, INFO, "Skipped WAR\n"); + } + } + + if (!fsp_for_next_freq) { + mr13_flip_fspwr = (next->emc_mrw3 & 0xffffff3f) | 0x80; + mr13_flip_fspop = (next->emc_mrw3 & 0xffffff3f) | 0x00; + } else { + mr13_flip_fspwr = (next->emc_mrw3 & 0xffffff3f) | 0x40; + mr13_flip_fspop = (next->emc_mrw3 & 0xffffff3f) | 0xc0; + } + + if (dram_type == DRAM_TYPE_LPDDR4) { + emc_writel(emc, mr13_flip_fspwr, EMC_MRW3); + emc_writel(emc, next->emc_mrw, EMC_MRW); + emc_writel(emc, next->emc_mrw2, EMC_MRW2); + } + + /* + * Step 8: + * Program the shadow registers. + */ + emc_dbg(emc, STEPS, "Step 8\n"); + emc_dbg(emc, SUB_STEPS, "Writing burst_regs\n"); + + for (i = 0; i < next->num_burst; i++) { + const u16 *offsets = emc->offsets->burst; + u16 offset; + + if (!offsets[i]) + continue; + + value = next->burst_regs[i]; + offset = offsets[i]; + + if (dram_type != DRAM_TYPE_LPDDR4 && + (offset == EMC_MRW6 || offset == EMC_MRW7 || + offset == EMC_MRW8 || offset == EMC_MRW9 || + offset == EMC_MRW10 || offset == EMC_MRW11 || + offset == EMC_MRW12 || offset == EMC_MRW13 || + offset == EMC_MRW14 || offset == EMC_MRW15 || + offset == EMC_TRAINING_CTRL)) + continue; + + /* Pain... And suffering. */ + if (offset == EMC_CFG) { + value &= ~EMC_CFG_DRAM_ACPD; + value &= ~EMC_CFG_DYN_SELF_REF; + + if (dram_type == DRAM_TYPE_LPDDR4) { + value &= ~EMC_CFG_DRAM_CLKSTOP_SR; + value &= ~EMC_CFG_DRAM_CLKSTOP_PD; + } + } else if (offset == EMC_MRS_WAIT_CNT && + dram_type == DRAM_TYPE_LPDDR2 && + opt_zcal_en_cc && !opt_cc_short_zcal && + opt_short_zcal) { + value = (value & ~(EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK << + EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT)) | + ((zq_wait_long & EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK) << + EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT); + } else if (offset == EMC_ZCAL_WAIT_CNT && + dram_type == DRAM_TYPE_DDR3 && opt_zcal_en_cc && + !opt_cc_short_zcal && opt_short_zcal) { + value = (value & ~(EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK << + EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_SHIFT)) | + ((zq_wait_long & EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK) << + EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT); + } else if (offset == EMC_ZCAL_INTERVAL && opt_zcal_en_cc) { + value = 0; /* EMC_ZCAL_INTERVAL reset value. */ + } else if (offset == EMC_PMACRO_AUTOCAL_CFG_COMMON) { + value |= EMC_PMACRO_AUTOCAL_CFG_COMMON_E_CAL_BYPASS_DVFS; + } else if (offset == EMC_PMACRO_DATA_PAD_TX_CTRL) { + value &= ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC); + } else if (offset == EMC_PMACRO_CMD_PAD_TX_CTRL) { + value |= EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON; + value &= ~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC); + } else if (offset == EMC_PMACRO_BRICK_CTRL_RFU1) { + value &= 0xf800f800; + } else if (offset == EMC_PMACRO_COMMON_PAD_TX_CTRL) { + value &= 0xfffffff0; + } + + emc_writel(emc, value, offset); + } + + /* SW addition: do EMC refresh adjustment here. */ + tegra210_emc_adjust_timing(emc, next); + + if (dram_type == DRAM_TYPE_LPDDR4) { + value = (23 << EMC_MRW_MRW_MA_SHIFT) | + (next->run_clocks & EMC_MRW_MRW_OP_MASK); + emc_writel(emc, value, EMC_MRW); + } + + /* Per channel burst registers. */ + emc_dbg(emc, SUB_STEPS, "Writing burst_regs_per_ch\n"); + + for (i = 0; i < next->num_burst_per_ch; i++) { + const struct tegra210_emc_per_channel_regs *burst = + emc->offsets->burst_per_channel; + + if (!burst[i].offset) + continue; + + if (dram_type != DRAM_TYPE_LPDDR4 && + (burst[i].offset == EMC_MRW6 || + burst[i].offset == EMC_MRW7 || + burst[i].offset == EMC_MRW8 || + burst[i].offset == EMC_MRW9 || + burst[i].offset == EMC_MRW10 || + burst[i].offset == EMC_MRW11 || + burst[i].offset == EMC_MRW12 || + burst[i].offset == EMC_MRW13 || + burst[i].offset == EMC_MRW14 || + burst[i].offset == EMC_MRW15)) + continue; + + /* Filter out second channel if not in DUAL_CHANNEL mode. */ + if (emc->num_channels < 2 && burst[i].bank >= 1) + continue; + + emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, + next->burst_reg_per_ch[i], burst[i].offset); + emc_channel_writel(emc, burst[i].bank, + next->burst_reg_per_ch[i], + burst[i].offset); + } + + /* Vref regs. */ + emc_dbg(emc, SUB_STEPS, "Writing vref_regs\n"); + + for (i = 0; i < next->vref_num; i++) { + const struct tegra210_emc_per_channel_regs *vref = + emc->offsets->vref_per_channel; + + if (!vref[i].offset) + continue; + + if (emc->num_channels < 2 && vref[i].bank >= 1) + continue; + + emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, + next->vref_perch_regs[i], vref[i].offset); + emc_channel_writel(emc, vref[i].bank, next->vref_perch_regs[i], + vref[i].offset); + } + + /* Trimmers. */ + emc_dbg(emc, SUB_STEPS, "Writing trim_regs\n"); + + for (i = 0; i < next->num_trim; i++) { + const u16 *offsets = emc->offsets->trim; + + if (!offsets[i]) + continue; + + if (compensate_trimmer_applicable && + (offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 || + offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 || + offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 || + offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 || + offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 || + offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 || + offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 || + offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 || + offsets[i] == EMC_DATA_BRLSHFT_0 || + offsets[i] == EMC_DATA_BRLSHFT_1)) { + value = tegra210_emc_compensate(next, offsets[i]); + emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, + value, offsets[i]); + emc_dbg(emc, EMA_WRITES, "0x%08x <= 0x%08x\n", + (u32)(u64)offsets[i], value); + emc_writel(emc, value, offsets[i]); + } else { + emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, + next->trim_regs[i], offsets[i]); + emc_writel(emc, next->trim_regs[i], offsets[i]); + } + } + + /* Per channel trimmers. */ + emc_dbg(emc, SUB_STEPS, "Writing trim_regs_per_ch\n"); + + for (i = 0; i < next->num_trim_per_ch; i++) { + const struct tegra210_emc_per_channel_regs *trim = + &emc->offsets->trim_per_channel[0]; + unsigned int offset; + + if (!trim[i].offset) + continue; + + if (emc->num_channels < 2 && trim[i].bank >= 1) + continue; + + offset = trim[i].offset; + + if (compensate_trimmer_applicable && + (offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 || + offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 || + offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 || + offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 || + offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 || + offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 || + offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 || + offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 || + offset == EMC_DATA_BRLSHFT_0 || + offset == EMC_DATA_BRLSHFT_1)) { + value = tegra210_emc_compensate(next, offset); + emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, + value, offset); + emc_dbg(emc, EMA_WRITES, "0x%08x <= 0x%08x\n", offset, + value); + emc_channel_writel(emc, trim[i].bank, value, offset); + } else { + emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, + next->trim_perch_regs[i], offset); + emc_channel_writel(emc, trim[i].bank, + next->trim_perch_regs[i], offset); + } + } + + emc_dbg(emc, SUB_STEPS, "Writing burst_mc_regs\n"); + + for (i = 0; i < next->num_mc_regs; i++) { + const u16 *offsets = emc->offsets->burst_mc; + u32 *values = next->burst_mc_regs; + + emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, + values[i], offsets[i]); + mc_writel(emc->mc, values[i], offsets[i]); + } + + /* Registers to be programmed on the faster clock. */ + if (next->rate < last->rate) { + const u16 *la = emc->offsets->la_scale; + + emc_dbg(emc, SUB_STEPS, "Writing la_scale_regs\n"); + + for (i = 0; i < next->num_up_down; i++) { + emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, + next->la_scale_regs[i], la[i]); + mc_writel(emc->mc, next->la_scale_regs[i], la[i]); + } + } + + /* Flush all the burst register writes. */ + mc_readl(emc->mc, MC_EMEM_ADR_CFG); + + /* + * Step 9: + * LPDDR4 section A. + */ + emc_dbg(emc, STEPS, "Step 9\n"); + + value = next->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX]; + value &= ~EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK; + + if (dram_type == DRAM_TYPE_LPDDR4) { + emc_writel(emc, 0, EMC_ZCAL_INTERVAL); + emc_writel(emc, value, EMC_ZCAL_WAIT_CNT); + + value = emc_dbg | (EMC_DBG_WRITE_MUX_ACTIVE | + EMC_DBG_WRITE_ACTIVE_ONLY); + + emc_writel(emc, value, EMC_DBG); + emc_writel(emc, 0, EMC_ZCAL_INTERVAL); + emc_writel(emc, emc_dbg, EMC_DBG); + } + + /* + * Step 10: + * LPDDR4 and DDR3 common section. + */ + emc_dbg(emc, STEPS, "Step 10\n"); + + if (opt_dvfs_mode == MAN_SR || dram_type == DRAM_TYPE_LPDDR4) { + if (dram_type == DRAM_TYPE_LPDDR4) + ccfifo_writel(emc, 0x101, EMC_SELF_REF, 0); + else + ccfifo_writel(emc, 0x1, EMC_SELF_REF, 0); + + if (dram_type == DRAM_TYPE_LPDDR4 && + dst_clk_period <= zqcal_before_cc_cutoff) { + ccfifo_writel(emc, mr13_flip_fspwr ^ 0x40, EMC_MRW3, 0); + ccfifo_writel(emc, (next->burst_regs[EMC_MRW6_INDEX] & + 0xFFFF3F3F) | + (last->burst_regs[EMC_MRW6_INDEX] & + 0x0000C0C0), EMC_MRW6, 0); + ccfifo_writel(emc, (next->burst_regs[EMC_MRW14_INDEX] & + 0xFFFF0707) | + (last->burst_regs[EMC_MRW14_INDEX] & + 0x00003838), EMC_MRW14, 0); + + if (emc->num_devices > 1) { + ccfifo_writel(emc, + (next->burst_regs[EMC_MRW7_INDEX] & + 0xFFFF3F3F) | + (last->burst_regs[EMC_MRW7_INDEX] & + 0x0000C0C0), EMC_MRW7, 0); + ccfifo_writel(emc, + (next->burst_regs[EMC_MRW15_INDEX] & + 0xFFFF0707) | + (last->burst_regs[EMC_MRW15_INDEX] & + 0x00003838), EMC_MRW15, 0); + } + + if (opt_zcal_en_cc) { + if (emc->num_devices < 2) + ccfifo_writel(emc, + 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT + | EMC_ZQ_CAL_ZQ_CAL_CMD, + EMC_ZQ_CAL, 0); + else if (shared_zq_resistor) + ccfifo_writel(emc, + 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT + | EMC_ZQ_CAL_ZQ_CAL_CMD, + EMC_ZQ_CAL, 0); + else + ccfifo_writel(emc, + EMC_ZQ_CAL_ZQ_CAL_CMD, + EMC_ZQ_CAL, 0); + } + } + } + + if (dram_type == DRAM_TYPE_LPDDR4) { + value = (1000 * fake->dram_timings[T_RP]) / src_clk_period; + ccfifo_writel(emc, mr13_flip_fspop | 0x8, EMC_MRW3, value); + ccfifo_writel(emc, 0, 0, tFC_lpddr4 / src_clk_period); + } + + if (dram_type == DRAM_TYPE_LPDDR4 || opt_dvfs_mode != MAN_SR) { + delay = 30; + + if (cya_allow_ref_cc) { + delay += (1000 * fake->dram_timings[T_RP]) / + src_clk_period; + delay += 4000 * fake->dram_timings[T_RFC]; + } + + ccfifo_writel(emc, emc_pin & ~(EMC_PIN_PIN_CKE_PER_DEV | + EMC_PIN_PIN_CKEB | + EMC_PIN_PIN_CKE), + EMC_PIN, delay); + } + + /* calculate reference delay multiplier */ + value = 1; + + if (ref_b4_sref_en) + value++; + + if (cya_allow_ref_cc) + value++; + + if (cya_issue_pc_ref) + value++; + + if (dram_type != DRAM_TYPE_LPDDR4) { + delay = ((1000 * fake->dram_timings[T_RP] / src_clk_period) + + (1000 * fake->dram_timings[T_RFC] / src_clk_period)); + delay = value * delay + 20; + } else { + delay = 0; + } + + /* + * Step 11: + * Ramp down. + */ + emc_dbg(emc, STEPS, "Step 11\n"); + + ccfifo_writel(emc, 0x0, EMC_CFG_SYNC, delay); + + value = emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE | EMC_DBG_WRITE_ACTIVE_ONLY; + ccfifo_writel(emc, value, EMC_DBG, 0); + + ramp_down_wait = tegra210_emc_dvfs_power_ramp_down(emc, src_clk_period, + 0); + + /* + * Step 12: + * And finally - trigger the clock change. + */ + emc_dbg(emc, STEPS, "Step 12\n"); + + ccfifo_writel(emc, 1, EMC_STALL_THEN_EXE_AFTER_CLKCHANGE, 0); + value &= ~EMC_DBG_WRITE_ACTIVE_ONLY; + ccfifo_writel(emc, value, EMC_DBG, 0); + + /* + * Step 13: + * Ramp up. + */ + emc_dbg(emc, STEPS, "Step 13\n"); + + ramp_up_wait = tegra210_emc_dvfs_power_ramp_up(emc, dst_clk_period, 0); + ccfifo_writel(emc, emc_dbg, EMC_DBG, 0); + + /* + * Step 14: + * Bringup CKE pins. + */ + emc_dbg(emc, STEPS, "Step 14\n"); + + if (dram_type == DRAM_TYPE_LPDDR4) { + value = emc_pin | EMC_PIN_PIN_CKE; + + if (emc->num_devices <= 1) + value &= ~(EMC_PIN_PIN_CKEB | EMC_PIN_PIN_CKE_PER_DEV); + else + value |= EMC_PIN_PIN_CKEB | EMC_PIN_PIN_CKE_PER_DEV; + + ccfifo_writel(emc, value, EMC_PIN, 0); + } + + /* + * Step 15: (two step 15s ??) + * Calculate zqlatch wait time; has dependency on ramping times. + */ + emc_dbg(emc, STEPS, "Step 15\n"); + + if (dst_clk_period <= zqcal_before_cc_cutoff) { + s32 t = (s32)(ramp_up_wait + ramp_down_wait) / + (s32)dst_clk_period; + zq_latch_dvfs_wait_time = (s32)tZQCAL_lpddr4_fc_adj - t; + } else { + zq_latch_dvfs_wait_time = tZQCAL_lpddr4_fc_adj - + div_o3(1000 * next->dram_timings[T_PDEX], + dst_clk_period); + } + + emc_dbg(emc, INFO, "tZQCAL_lpddr4_fc_adj = %u\n", tZQCAL_lpddr4_fc_adj); + emc_dbg(emc, INFO, "dst_clk_period = %u\n", + dst_clk_period); + emc_dbg(emc, INFO, "next->dram_timings[T_PDEX] = %u\n", + next->dram_timings[T_PDEX]); + emc_dbg(emc, INFO, "zq_latch_dvfs_wait_time = %d\n", + max_t(s32, 0, zq_latch_dvfs_wait_time)); + + if (dram_type == DRAM_TYPE_LPDDR4 && opt_zcal_en_cc) { + delay = div_o3(1000 * next->dram_timings[T_PDEX], + dst_clk_period); + + if (emc->num_devices < 2) { + if (dst_clk_period > zqcal_before_cc_cutoff) + ccfifo_writel(emc, + 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | + EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL, + delay); + + value = (mr13_flip_fspop & 0xfffffff7) | 0x0c000000; + ccfifo_writel(emc, value, EMC_MRW3, delay); + ccfifo_writel(emc, 0, EMC_SELF_REF, 0); + ccfifo_writel(emc, 0, EMC_REF, 0); + ccfifo_writel(emc, 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | + EMC_ZQ_CAL_ZQ_LATCH_CMD, + EMC_ZQ_CAL, + max_t(s32, 0, zq_latch_dvfs_wait_time)); + } else if (shared_zq_resistor) { + if (dst_clk_period > zqcal_before_cc_cutoff) + ccfifo_writel(emc, + 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | + EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL, + delay); + + ccfifo_writel(emc, 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | + EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL, + max_t(s32, 0, zq_latch_dvfs_wait_time) + + delay); + ccfifo_writel(emc, 1UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | + EMC_ZQ_CAL_ZQ_LATCH_CMD, + EMC_ZQ_CAL, 0); + + value = (mr13_flip_fspop & 0xfffffff7) | 0x0c000000; + ccfifo_writel(emc, value, EMC_MRW3, 0); + ccfifo_writel(emc, 0, EMC_SELF_REF, 0); + ccfifo_writel(emc, 0, EMC_REF, 0); + + ccfifo_writel(emc, 1UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | + EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL, + tZQCAL_lpddr4 / dst_clk_period); + } else { + if (dst_clk_period > zqcal_before_cc_cutoff) + ccfifo_writel(emc, EMC_ZQ_CAL_ZQ_CAL_CMD, + EMC_ZQ_CAL, delay); + + value = (mr13_flip_fspop & 0xfffffff7) | 0x0c000000; + ccfifo_writel(emc, value, EMC_MRW3, delay); + ccfifo_writel(emc, 0, EMC_SELF_REF, 0); + ccfifo_writel(emc, 0, EMC_REF, 0); + + ccfifo_writel(emc, EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL, + max_t(s32, 0, zq_latch_dvfs_wait_time)); + } + } + + /* WAR: delay for zqlatch */ + ccfifo_writel(emc, 0, 0, 10); + + /* + * Step 16: + * LPDDR4 Conditional Training Kickoff. Removed. + */ + + /* + * Step 17: + * MANSR exit self refresh. + */ + emc_dbg(emc, STEPS, "Step 17\n"); + + if (opt_dvfs_mode == MAN_SR && dram_type != DRAM_TYPE_LPDDR4) + ccfifo_writel(emc, 0, EMC_SELF_REF, 0); + + /* + * Step 18: + * Send MRWs to LPDDR3/DDR3. + */ + emc_dbg(emc, STEPS, "Step 18\n"); + + if (dram_type == DRAM_TYPE_LPDDR2) { + ccfifo_writel(emc, next->emc_mrw2, EMC_MRW2, 0); + ccfifo_writel(emc, next->emc_mrw, EMC_MRW, 0); + if (is_lpddr3) + ccfifo_writel(emc, next->emc_mrw4, EMC_MRW4, 0); + } else if (dram_type == DRAM_TYPE_DDR3) { + if (opt_dll_mode) + ccfifo_writel(emc, next->emc_emrs & + ~EMC_EMRS_USE_EMRS_LONG_CNT, EMC_EMRS, 0); + ccfifo_writel(emc, next->emc_emrs2 & + ~EMC_EMRS2_USE_EMRS2_LONG_CNT, EMC_EMRS2, 0); + ccfifo_writel(emc, next->emc_mrs | + EMC_EMRS_USE_EMRS_LONG_CNT, EMC_MRS, 0); + } + + /* + * Step 19: + * ZQCAL for LPDDR3/DDR3 + */ + emc_dbg(emc, STEPS, "Step 19\n"); + + if (opt_zcal_en_cc) { + if (dram_type == DRAM_TYPE_LPDDR2) { + value = opt_cc_short_zcal ? 90000 : 360000; + value = div_o3(value, dst_clk_period); + value = value << + EMC_MRS_WAIT_CNT2_MRS_EXT2_WAIT_CNT_SHIFT | + value << + EMC_MRS_WAIT_CNT2_MRS_EXT1_WAIT_CNT_SHIFT; + ccfifo_writel(emc, value, EMC_MRS_WAIT_CNT2, 0); + + value = opt_cc_short_zcal ? 0x56 : 0xab; + ccfifo_writel(emc, 2 << EMC_MRW_MRW_DEV_SELECTN_SHIFT | + EMC_MRW_USE_MRW_EXT_CNT | + 10 << EMC_MRW_MRW_MA_SHIFT | + value << EMC_MRW_MRW_OP_SHIFT, + EMC_MRW, 0); + + if (emc->num_devices > 1) { + value = 1 << EMC_MRW_MRW_DEV_SELECTN_SHIFT | + EMC_MRW_USE_MRW_EXT_CNT | + 10 << EMC_MRW_MRW_MA_SHIFT | + value << EMC_MRW_MRW_OP_SHIFT; + ccfifo_writel(emc, value, EMC_MRW, 0); + } + } else if (dram_type == DRAM_TYPE_DDR3) { + value = opt_cc_short_zcal ? 0 : EMC_ZQ_CAL_LONG; + + ccfifo_writel(emc, value | + 2 << EMC_ZQ_CAL_DEV_SEL_SHIFT | + EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL, + 0); + + if (emc->num_devices > 1) { + value = value | 1 << EMC_ZQ_CAL_DEV_SEL_SHIFT | + EMC_ZQ_CAL_ZQ_CAL_CMD; + ccfifo_writel(emc, value, EMC_ZQ_CAL, 0); + } + } + } + + if (bg_reg_mode_change) { + tegra210_emc_set_shadow_bypass(emc, ACTIVE); + + if (ramp_up_wait <= 1250000) + delay = (1250000 - ramp_up_wait) / dst_clk_period; + else + delay = 0; + + ccfifo_writel(emc, + next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX], + EMC_PMACRO_BG_BIAS_CTRL_0, delay); + tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); + } + + /* + * Step 20: + * Issue ref and optional QRST. + */ + emc_dbg(emc, STEPS, "Step 20\n"); + + if (dram_type != DRAM_TYPE_LPDDR4) + ccfifo_writel(emc, 0, EMC_REF, 0); + + if (opt_do_sw_qrst) { + ccfifo_writel(emc, 1, EMC_ISSUE_QRST, 0); + ccfifo_writel(emc, 0, EMC_ISSUE_QRST, 2); + } + + /* + * Step 21: + * Restore ZCAL and ZCAL interval. + */ + emc_dbg(emc, STEPS, "Step 21\n"); + + if (save_restore_clkstop_pd || opt_zcal_en_cc) { + ccfifo_writel(emc, emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE, + EMC_DBG, 0); + if (opt_zcal_en_cc && dram_type != DRAM_TYPE_LPDDR4) + ccfifo_writel(emc, next->burst_regs[EMC_ZCAL_INTERVAL_INDEX], + EMC_ZCAL_INTERVAL, 0); + + if (save_restore_clkstop_pd) + ccfifo_writel(emc, next->burst_regs[EMC_CFG_INDEX] & + ~EMC_CFG_DYN_SELF_REF, + EMC_CFG, 0); + ccfifo_writel(emc, emc_dbg, EMC_DBG, 0); + } + + /* + * Step 22: + * Restore EMC_CFG_PIPE_CLK. + */ + emc_dbg(emc, STEPS, "Step 22\n"); + + ccfifo_writel(emc, emc_cfg_pipe_clk, EMC_CFG_PIPE_CLK, 0); + + if (bg_reg_mode_change) { + if (enable_bg_reg) + emc_writel(emc, + next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + ~EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD, + EMC_PMACRO_BG_BIAS_CTRL_0); + else + emc_writel(emc, + next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & + ~EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD, + EMC_PMACRO_BG_BIAS_CTRL_0); + } + + /* + * Step 23: + */ + emc_dbg(emc, STEPS, "Step 23\n"); + + value = emc_readl(emc, EMC_CFG_DIG_DLL); + value |= EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC; + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK; + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK; + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN; + value = (value & ~EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK) | + (2 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT); + emc_writel(emc, value, EMC_CFG_DIG_DLL); + + tegra210_emc_do_clock_change(emc, clksrc); + + /* + * Step 24: + * Save training results. Removed. + */ + + /* + * Step 25: + * Program MC updown registers. + */ + emc_dbg(emc, STEPS, "Step 25\n"); + + if (next->rate > last->rate) { + for (i = 0; i < next->num_up_down; i++) + mc_writel(emc->mc, next->la_scale_regs[i], + emc->offsets->la_scale[i]); + + tegra210_emc_timing_update(emc); + } + + /* + * Step 26: + * Restore ZCAL registers. + */ + emc_dbg(emc, STEPS, "Step 26\n"); + + if (dram_type == DRAM_TYPE_LPDDR4) { + tegra210_emc_set_shadow_bypass(emc, ACTIVE); + emc_writel(emc, next->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX], + EMC_ZCAL_WAIT_CNT); + emc_writel(emc, next->burst_regs[EMC_ZCAL_INTERVAL_INDEX], + EMC_ZCAL_INTERVAL); + tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); + } + + if (dram_type != DRAM_TYPE_LPDDR4 && opt_zcal_en_cc && + !opt_short_zcal && opt_cc_short_zcal) { + udelay(2); + + tegra210_emc_set_shadow_bypass(emc, ACTIVE); + if (dram_type == DRAM_TYPE_LPDDR2) + emc_writel(emc, next->burst_regs[EMC_MRS_WAIT_CNT_INDEX], + EMC_MRS_WAIT_CNT); + else if (dram_type == DRAM_TYPE_DDR3) + emc_writel(emc, next->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX], + EMC_ZCAL_WAIT_CNT); + tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); + } + + /* + * Step 27: + * Restore EMC_CFG, FDPD registers. + */ + emc_dbg(emc, STEPS, "Step 27\n"); + + tegra210_emc_set_shadow_bypass(emc, ACTIVE); + emc_writel(emc, next->burst_regs[EMC_CFG_INDEX], EMC_CFG); + tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); + emc_writel(emc, next->emc_fdpd_ctrl_cmd_no_ramp, + EMC_FDPD_CTRL_CMD_NO_RAMP); + emc_writel(emc, next->emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL); + + /* + * Step 28: + * Training recover. Removed. + */ + emc_dbg(emc, STEPS, "Step 28\n"); + + tegra210_emc_set_shadow_bypass(emc, ACTIVE); + emc_writel(emc, + next->burst_regs[EMC_PMACRO_AUTOCAL_CFG_COMMON_INDEX], + EMC_PMACRO_AUTOCAL_CFG_COMMON); + tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); + + /* + * Step 29: + * Power fix WAR. + */ + emc_dbg(emc, STEPS, "Step 29\n"); + + emc_writel(emc, EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0 | + EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1 | + EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2 | + EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3 | + EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4 | + EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5 | + EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6 | + EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, + EMC_PMACRO_CFG_PM_GLOBAL_0); + emc_writel(emc, EMC_PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR, + EMC_PMACRO_TRAINING_CTRL_0); + emc_writel(emc, EMC_PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR, + EMC_PMACRO_TRAINING_CTRL_1); + emc_writel(emc, 0, EMC_PMACRO_CFG_PM_GLOBAL_0); + + /* + * Step 30: + * Re-enable autocal. + */ + emc_dbg(emc, STEPS, "Step 30: Re-enable DLL and AUTOCAL\n"); + + if (next->burst_regs[EMC_CFG_DIG_DLL_INDEX] & EMC_CFG_DIG_DLL_CFG_DLL_EN) { + value = emc_readl(emc, EMC_CFG_DIG_DLL); + value |= EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC; + value |= EMC_CFG_DIG_DLL_CFG_DLL_EN; + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK; + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK; + value = (value & ~EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK) | + (2 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT); + emc_writel(emc, value, EMC_CFG_DIG_DLL); + tegra210_emc_timing_update(emc); + } + + emc_writel(emc, next->emc_auto_cal_config, EMC_AUTO_CAL_CONFIG); + + /* Done! Yay. */ +} + +const struct tegra210_emc_sequence tegra210_emc_r21021 = { + .revision = 0x7, + .set_clock = tegra210_emc_r21021_set_clock, + .periodic_compensation = tegra210_emc_r21021_periodic_compensation, +}; diff --git a/drivers/memory/tegra/tegra210-emc-core.c b/drivers/memory/tegra/tegra210-emc-core.c new file mode 100644 index 000000000000..cdd663ba4733 --- /dev/null +++ b/drivers/memory/tegra/tegra210-emc-core.c @@ -0,0 +1,2100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/clk/tegra.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> +#include <linux/slab.h> +#include <linux/thermal.h> +#include <soc/tegra/fuse.h> +#include <soc/tegra/mc.h> + +#include "tegra210-emc.h" +#include "tegra210-mc.h" + +/* CLK_RST_CONTROLLER_CLK_SOURCE_EMC */ +#define EMC_CLK_EMC_2X_CLK_SRC_SHIFT 29 +#define EMC_CLK_EMC_2X_CLK_SRC_MASK \ + (0x7 << EMC_CLK_EMC_2X_CLK_SRC_SHIFT) +#define EMC_CLK_SOURCE_PLLM_LJ 0x4 +#define EMC_CLK_SOURCE_PLLMB_LJ 0x5 +#define EMC_CLK_FORCE_CC_TRIGGER BIT(27) +#define EMC_CLK_MC_EMC_SAME_FREQ BIT(16) +#define EMC_CLK_EMC_2X_CLK_DIVISOR_SHIFT 0 +#define EMC_CLK_EMC_2X_CLK_DIVISOR_MASK \ + (0xff << EMC_CLK_EMC_2X_CLK_DIVISOR_SHIFT) + +/* CLK_RST_CONTROLLER_CLK_SOURCE_EMC_DLL */ +#define DLL_CLK_EMC_DLL_CLK_SRC_SHIFT 29 +#define DLL_CLK_EMC_DLL_CLK_SRC_MASK \ + (0x7 << DLL_CLK_EMC_DLL_CLK_SRC_SHIFT) +#define DLL_CLK_EMC_DLL_DDLL_CLK_SEL_SHIFT 10 +#define DLL_CLK_EMC_DLL_DDLL_CLK_SEL_MASK \ + (0x3 << DLL_CLK_EMC_DLL_DDLL_CLK_SEL_SHIFT) +#define PLLM_VCOA 0 +#define PLLM_VCOB 1 +#define EMC_DLL_SWITCH_OUT 2 +#define DLL_CLK_EMC_DLL_CLK_DIVISOR_SHIFT 0 +#define DLL_CLK_EMC_DLL_CLK_DIVISOR_MASK \ + (0xff << DLL_CLK_EMC_DLL_CLK_DIVISOR_SHIFT) + +/* MC_EMEM_ARB_MISC0 */ +#define MC_EMEM_ARB_MISC0_EMC_SAME_FREQ BIT(27) + +/* EMC_DATA_BRLSHFT_X */ +#define EMC0_EMC_DATA_BRLSHFT_0_INDEX 2 +#define EMC1_EMC_DATA_BRLSHFT_0_INDEX 3 +#define EMC0_EMC_DATA_BRLSHFT_1_INDEX 4 +#define EMC1_EMC_DATA_BRLSHFT_1_INDEX 5 + +#define TRIM_REG(chan, rank, reg, byte) \ + (((EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## reg ## \ + _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte ## _MASK & \ + next->trim_regs[EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## \ + rank ## _ ## reg ## _INDEX]) >> \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## reg ## \ + _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte ## _SHIFT) \ + + \ + (((EMC_DATA_BRLSHFT_ ## rank ## _RANK ## rank ## _BYTE ## \ + byte ## _DATA_BRLSHFT_MASK & \ + next->trim_perch_regs[EMC ## chan ## \ + _EMC_DATA_BRLSHFT_ ## rank ## _INDEX]) >> \ + EMC_DATA_BRLSHFT_ ## rank ## _RANK ## rank ## _BYTE ## \ + byte ## _DATA_BRLSHFT_SHIFT) * 64)) + +#define CALC_TEMP(rank, reg, byte1, byte2, n) \ + (((new[n] << EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## \ + reg ## _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte1 ## _SHIFT) & \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## reg ## \ + _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte1 ## _MASK) \ + | \ + ((new[n + 1] << EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ##\ + reg ## _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte2 ## _SHIFT) & \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## reg ## \ + _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte2 ## _MASK)) + +#define REFRESH_SPEEDUP(value, speedup) \ + (((value) & 0xffff0000) | ((value) & 0xffff) * (speedup)) + +#define LPDDR2_MR4_SRR GENMASK(2, 0) + +static const struct tegra210_emc_sequence *tegra210_emc_sequences[] = { + &tegra210_emc_r21021, +}; + +static const struct tegra210_emc_table_register_offsets +tegra210_emc_table_register_offsets = { + .burst = { + EMC_RC, + EMC_RFC, + EMC_RFCPB, + EMC_REFCTRL2, + EMC_RFC_SLR, + EMC_RAS, + EMC_RP, + EMC_R2W, + EMC_W2R, + EMC_R2P, + EMC_W2P, + EMC_R2R, + EMC_TPPD, + EMC_CCDMW, + EMC_RD_RCD, + EMC_WR_RCD, + EMC_RRD, + EMC_REXT, + EMC_WEXT, + EMC_WDV_CHK, + EMC_WDV, + EMC_WSV, + EMC_WEV, + EMC_WDV_MASK, + EMC_WS_DURATION, + EMC_WE_DURATION, + EMC_QUSE, + EMC_QUSE_WIDTH, + EMC_IBDLY, + EMC_OBDLY, + EMC_EINPUT, + EMC_MRW6, + EMC_EINPUT_DURATION, + EMC_PUTERM_EXTRA, + EMC_PUTERM_WIDTH, + EMC_QRST, + EMC_QSAFE, + EMC_RDV, + EMC_RDV_MASK, + EMC_RDV_EARLY, + EMC_RDV_EARLY_MASK, + EMC_REFRESH, + EMC_BURST_REFRESH_NUM, + EMC_PRE_REFRESH_REQ_CNT, + EMC_PDEX2WR, + EMC_PDEX2RD, + EMC_PCHG2PDEN, + EMC_ACT2PDEN, + EMC_AR2PDEN, + EMC_RW2PDEN, + EMC_CKE2PDEN, + EMC_PDEX2CKE, + EMC_PDEX2MRR, + EMC_TXSR, + EMC_TXSRDLL, + EMC_TCKE, + EMC_TCKESR, + EMC_TPD, + EMC_TFAW, + EMC_TRPAB, + EMC_TCLKSTABLE, + EMC_TCLKSTOP, + EMC_MRW7, + EMC_TREFBW, + EMC_ODT_WRITE, + EMC_FBIO_CFG5, + EMC_FBIO_CFG7, + EMC_CFG_DIG_DLL, + EMC_CFG_DIG_DLL_PERIOD, + EMC_PMACRO_IB_RXRT, + EMC_CFG_PIPE_1, + EMC_CFG_PIPE_2, + EMC_PMACRO_QUSE_DDLL_RANK0_4, + EMC_PMACRO_QUSE_DDLL_RANK0_5, + EMC_PMACRO_QUSE_DDLL_RANK1_4, + EMC_PMACRO_QUSE_DDLL_RANK1_5, + EMC_MRW8, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4, + EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5, + EMC_PMACRO_DDLL_LONG_CMD_0, + EMC_PMACRO_DDLL_LONG_CMD_1, + EMC_PMACRO_DDLL_LONG_CMD_2, + EMC_PMACRO_DDLL_LONG_CMD_3, + EMC_PMACRO_DDLL_LONG_CMD_4, + EMC_PMACRO_DDLL_SHORT_CMD_0, + EMC_PMACRO_DDLL_SHORT_CMD_1, + EMC_PMACRO_DDLL_SHORT_CMD_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_3, + EMC_TXDSRVTTGEN, + EMC_FDPD_CTRL_DQ, + EMC_FDPD_CTRL_CMD, + EMC_FBIO_SPARE, + EMC_ZCAL_INTERVAL, + EMC_ZCAL_WAIT_CNT, + EMC_MRS_WAIT_CNT, + EMC_MRS_WAIT_CNT2, + EMC_AUTO_CAL_CHANNEL, + EMC_DLL_CFG_0, + EMC_DLL_CFG_1, + EMC_PMACRO_AUTOCAL_CFG_COMMON, + EMC_PMACRO_ZCTRL, + EMC_CFG, + EMC_CFG_PIPE, + EMC_DYN_SELF_REF_CONTROL, + EMC_QPOP, + EMC_DQS_BRLSHFT_0, + EMC_DQS_BRLSHFT_1, + EMC_CMD_BRLSHFT_2, + EMC_CMD_BRLSHFT_3, + EMC_PMACRO_PAD_CFG_CTRL, + EMC_PMACRO_DATA_PAD_RX_CTRL, + EMC_PMACRO_CMD_PAD_RX_CTRL, + EMC_PMACRO_DATA_RX_TERM_MODE, + EMC_PMACRO_CMD_RX_TERM_MODE, + EMC_PMACRO_CMD_PAD_TX_CTRL, + EMC_PMACRO_DATA_PAD_TX_CTRL, + EMC_PMACRO_COMMON_PAD_TX_CTRL, + EMC_PMACRO_VTTGEN_CTRL_0, + EMC_PMACRO_VTTGEN_CTRL_1, + EMC_PMACRO_VTTGEN_CTRL_2, + EMC_PMACRO_BRICK_CTRL_RFU1, + EMC_PMACRO_CMD_BRICK_CTRL_FDPD, + EMC_PMACRO_BRICK_CTRL_RFU2, + EMC_PMACRO_DATA_BRICK_CTRL_FDPD, + EMC_PMACRO_BG_BIAS_CTRL_0, + EMC_CFG_3, + EMC_PMACRO_TX_PWRD_0, + EMC_PMACRO_TX_PWRD_1, + EMC_PMACRO_TX_PWRD_2, + EMC_PMACRO_TX_PWRD_3, + EMC_PMACRO_TX_PWRD_4, + EMC_PMACRO_TX_PWRD_5, + EMC_CONFIG_SAMPLE_DELAY, + EMC_PMACRO_TX_SEL_CLK_SRC_0, + EMC_PMACRO_TX_SEL_CLK_SRC_1, + EMC_PMACRO_TX_SEL_CLK_SRC_2, + EMC_PMACRO_TX_SEL_CLK_SRC_3, + EMC_PMACRO_TX_SEL_CLK_SRC_4, + EMC_PMACRO_TX_SEL_CLK_SRC_5, + EMC_PMACRO_DDLL_BYPASS, + EMC_PMACRO_DDLL_PWRD_0, + EMC_PMACRO_DDLL_PWRD_1, + EMC_PMACRO_DDLL_PWRD_2, + EMC_PMACRO_CMD_CTRL_0, + EMC_PMACRO_CMD_CTRL_1, + EMC_PMACRO_CMD_CTRL_2, + EMC_TR_TIMING_0, + EMC_TR_DVFS, + EMC_TR_CTRL_1, + EMC_TR_RDV, + EMC_TR_QPOP, + EMC_TR_RDV_MASK, + EMC_MRW14, + EMC_TR_QSAFE, + EMC_TR_QRST, + EMC_TRAINING_CTRL, + EMC_TRAINING_SETTLE, + EMC_TRAINING_VREF_SETTLE, + EMC_TRAINING_CA_FINE_CTRL, + EMC_TRAINING_CA_CTRL_MISC, + EMC_TRAINING_CA_CTRL_MISC1, + EMC_TRAINING_CA_VREF_CTRL, + EMC_TRAINING_QUSE_CORS_CTRL, + EMC_TRAINING_QUSE_FINE_CTRL, + EMC_TRAINING_QUSE_CTRL_MISC, + EMC_TRAINING_QUSE_VREF_CTRL, + EMC_TRAINING_READ_FINE_CTRL, + EMC_TRAINING_READ_CTRL_MISC, + EMC_TRAINING_READ_VREF_CTRL, + EMC_TRAINING_WRITE_FINE_CTRL, + EMC_TRAINING_WRITE_CTRL_MISC, + EMC_TRAINING_WRITE_VREF_CTRL, + EMC_TRAINING_MPC, + EMC_MRW15, + }, + .trim = { + EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0, + EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1, + EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2, + EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3, + EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0, + EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1, + EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2, + EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_2, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_0, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_1, + EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_2, + EMC_PMACRO_IB_VREF_DQS_0, + EMC_PMACRO_IB_VREF_DQS_1, + EMC_PMACRO_IB_VREF_DQ_0, + EMC_PMACRO_IB_VREF_DQ_1, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_2, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_0, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_1, + EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_2, + EMC_PMACRO_QUSE_DDLL_RANK0_0, + EMC_PMACRO_QUSE_DDLL_RANK0_1, + EMC_PMACRO_QUSE_DDLL_RANK0_2, + EMC_PMACRO_QUSE_DDLL_RANK0_3, + EMC_PMACRO_QUSE_DDLL_RANK1_0, + EMC_PMACRO_QUSE_DDLL_RANK1_1, + EMC_PMACRO_QUSE_DDLL_RANK1_2, + EMC_PMACRO_QUSE_DDLL_RANK1_3 + }, + .burst_mc = { + MC_EMEM_ARB_CFG, + MC_EMEM_ARB_OUTSTANDING_REQ, + MC_EMEM_ARB_REFPB_HP_CTRL, + MC_EMEM_ARB_REFPB_BANK_CTRL, + MC_EMEM_ARB_TIMING_RCD, + MC_EMEM_ARB_TIMING_RP, + MC_EMEM_ARB_TIMING_RC, + MC_EMEM_ARB_TIMING_RAS, + MC_EMEM_ARB_TIMING_FAW, + MC_EMEM_ARB_TIMING_RRD, + MC_EMEM_ARB_TIMING_RAP2PRE, + MC_EMEM_ARB_TIMING_WAP2PRE, + MC_EMEM_ARB_TIMING_R2R, + MC_EMEM_ARB_TIMING_W2W, + MC_EMEM_ARB_TIMING_R2W, + MC_EMEM_ARB_TIMING_CCDMW, + MC_EMEM_ARB_TIMING_W2R, + MC_EMEM_ARB_TIMING_RFCPB, + MC_EMEM_ARB_DA_TURNS, + MC_EMEM_ARB_DA_COVERS, + MC_EMEM_ARB_MISC0, + MC_EMEM_ARB_MISC1, + MC_EMEM_ARB_MISC2, + MC_EMEM_ARB_RING1_THROTTLE, + MC_EMEM_ARB_DHYST_CTRL, + MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0, + MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1, + MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2, + MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3, + MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4, + MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5, + MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6, + MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7, + }, + .la_scale = { + MC_MLL_MPCORER_PTSA_RATE, + MC_FTOP_PTSA_RATE, + MC_PTSA_GRANT_DECREMENT, + MC_LATENCY_ALLOWANCE_XUSB_0, + MC_LATENCY_ALLOWANCE_XUSB_1, + MC_LATENCY_ALLOWANCE_TSEC_0, + MC_LATENCY_ALLOWANCE_SDMMCA_0, + MC_LATENCY_ALLOWANCE_SDMMCAA_0, + MC_LATENCY_ALLOWANCE_SDMMC_0, + MC_LATENCY_ALLOWANCE_SDMMCAB_0, + MC_LATENCY_ALLOWANCE_PPCS_0, + MC_LATENCY_ALLOWANCE_PPCS_1, + MC_LATENCY_ALLOWANCE_MPCORE_0, + MC_LATENCY_ALLOWANCE_HC_0, + MC_LATENCY_ALLOWANCE_HC_1, + MC_LATENCY_ALLOWANCE_AVPC_0, + MC_LATENCY_ALLOWANCE_GPU_0, + MC_LATENCY_ALLOWANCE_GPU2_0, + MC_LATENCY_ALLOWANCE_NVENC_0, + MC_LATENCY_ALLOWANCE_NVDEC_0, + MC_LATENCY_ALLOWANCE_VIC_0, + MC_LATENCY_ALLOWANCE_VI2_0, + MC_LATENCY_ALLOWANCE_ISP2_0, + MC_LATENCY_ALLOWANCE_ISP2_1, + }, + .burst_per_channel = { + { .bank = 0, .offset = EMC_MRW10, }, + { .bank = 1, .offset = EMC_MRW10, }, + { .bank = 0, .offset = EMC_MRW11, }, + { .bank = 1, .offset = EMC_MRW11, }, + { .bank = 0, .offset = EMC_MRW12, }, + { .bank = 1, .offset = EMC_MRW12, }, + { .bank = 0, .offset = EMC_MRW13, }, + { .bank = 1, .offset = EMC_MRW13, }, + }, + .trim_per_channel = { + { .bank = 0, .offset = EMC_CMD_BRLSHFT_0, }, + { .bank = 1, .offset = EMC_CMD_BRLSHFT_1, }, + { .bank = 0, .offset = EMC_DATA_BRLSHFT_0, }, + { .bank = 1, .offset = EMC_DATA_BRLSHFT_0, }, + { .bank = 0, .offset = EMC_DATA_BRLSHFT_1, }, + { .bank = 1, .offset = EMC_DATA_BRLSHFT_1, }, + { .bank = 0, .offset = EMC_QUSE_BRLSHFT_0, }, + { .bank = 1, .offset = EMC_QUSE_BRLSHFT_1, }, + { .bank = 0, .offset = EMC_QUSE_BRLSHFT_2, }, + { .bank = 1, .offset = EMC_QUSE_BRLSHFT_3, }, + }, + .vref_per_channel = { + { + .bank = 0, + .offset = EMC_TRAINING_OPT_DQS_IB_VREF_RANK0, + }, { + .bank = 1, + .offset = EMC_TRAINING_OPT_DQS_IB_VREF_RANK0, + }, { + .bank = 0, + .offset = EMC_TRAINING_OPT_DQS_IB_VREF_RANK1, + }, { + .bank = 1, + .offset = EMC_TRAINING_OPT_DQS_IB_VREF_RANK1, + }, + }, +}; + +static void tegra210_emc_train(struct timer_list *timer) +{ + struct tegra210_emc *emc = from_timer(emc, timer, training); + unsigned long flags; + + if (!emc->last) + return; + + spin_lock_irqsave(&emc->lock, flags); + + if (emc->sequence->periodic_compensation) + emc->sequence->periodic_compensation(emc); + + spin_unlock_irqrestore(&emc->lock, flags); + + mod_timer(&emc->training, + jiffies + msecs_to_jiffies(emc->training_interval)); +} + +static void tegra210_emc_training_start(struct tegra210_emc *emc) +{ + mod_timer(&emc->training, + jiffies + msecs_to_jiffies(emc->training_interval)); +} + +static void tegra210_emc_training_stop(struct tegra210_emc *emc) +{ + del_timer(&emc->training); +} + +static unsigned int tegra210_emc_get_temperature(struct tegra210_emc *emc) +{ + unsigned long flags; + u32 value, max = 0; + unsigned int i; + + spin_lock_irqsave(&emc->lock, flags); + + for (i = 0; i < emc->num_devices; i++) { + value = tegra210_emc_mrr_read(emc, i, 4); + + if (value & BIT(7)) + dev_dbg(emc->dev, + "sensor reading changed for device %u: %08x\n", + i, value); + + value = FIELD_GET(LPDDR2_MR4_SRR, value); + if (value > max) + max = value; + } + + spin_unlock_irqrestore(&emc->lock, flags); + + return max; +} + +static void tegra210_emc_poll_refresh(struct timer_list *timer) +{ + struct tegra210_emc *emc = from_timer(emc, timer, refresh_timer); + unsigned int temperature; + + if (!emc->debugfs.temperature) + temperature = tegra210_emc_get_temperature(emc); + else + temperature = emc->debugfs.temperature; + + if (temperature == emc->temperature) + goto reset; + + switch (temperature) { + case 0 ... 3: + /* temperature is fine, using regular refresh */ + dev_dbg(emc->dev, "switching to nominal refresh...\n"); + tegra210_emc_set_refresh(emc, TEGRA210_EMC_REFRESH_NOMINAL); + break; + + case 4: + dev_dbg(emc->dev, "switching to 2x refresh...\n"); + tegra210_emc_set_refresh(emc, TEGRA210_EMC_REFRESH_2X); + break; + + case 5: + dev_dbg(emc->dev, "switching to 4x refresh...\n"); + tegra210_emc_set_refresh(emc, TEGRA210_EMC_REFRESH_4X); + break; + + case 6 ... 7: + dev_dbg(emc->dev, "switching to throttle refresh...\n"); + tegra210_emc_set_refresh(emc, TEGRA210_EMC_REFRESH_THROTTLE); + break; + + default: + WARN(1, "invalid DRAM temperature state %u\n", temperature); + return; + } + + emc->temperature = temperature; + +reset: + if (atomic_read(&emc->refresh_poll) > 0) { + unsigned int interval = emc->refresh_poll_interval; + unsigned int timeout = msecs_to_jiffies(interval); + + mod_timer(&emc->refresh_timer, jiffies + timeout); + } +} + +static void tegra210_emc_poll_refresh_stop(struct tegra210_emc *emc) +{ + atomic_set(&emc->refresh_poll, 0); + del_timer_sync(&emc->refresh_timer); +} + +static void tegra210_emc_poll_refresh_start(struct tegra210_emc *emc) +{ + atomic_set(&emc->refresh_poll, 1); + + mod_timer(&emc->refresh_timer, + jiffies + msecs_to_jiffies(emc->refresh_poll_interval)); +} + +static int tegra210_emc_cd_max_state(struct thermal_cooling_device *cd, + unsigned long *state) +{ + *state = 1; + + return 0; +} + +static int tegra210_emc_cd_get_state(struct thermal_cooling_device *cd, + unsigned long *state) +{ + struct tegra210_emc *emc = cd->devdata; + + *state = atomic_read(&emc->refresh_poll); + + return 0; +} + +static int tegra210_emc_cd_set_state(struct thermal_cooling_device *cd, + unsigned long state) +{ + struct tegra210_emc *emc = cd->devdata; + + if (state == atomic_read(&emc->refresh_poll)) + return 0; + + if (state) + tegra210_emc_poll_refresh_start(emc); + else + tegra210_emc_poll_refresh_stop(emc); + + return 0; +} + +static struct thermal_cooling_device_ops tegra210_emc_cd_ops = { + .get_max_state = tegra210_emc_cd_max_state, + .get_cur_state = tegra210_emc_cd_get_state, + .set_cur_state = tegra210_emc_cd_set_state, +}; + +static void tegra210_emc_set_clock(struct tegra210_emc *emc, u32 clksrc) +{ + emc->sequence->set_clock(emc, clksrc); + + if (emc->next->periodic_training) + tegra210_emc_training_start(emc); + else + tegra210_emc_training_stop(emc); +} + +static void tegra210_change_dll_src(struct tegra210_emc *emc, + u32 clksrc) +{ + u32 dll_setting = emc->next->dll_clk_src; + u32 emc_clk_src; + u32 emc_clk_div; + + emc_clk_src = (clksrc & EMC_CLK_EMC_2X_CLK_SRC_MASK) >> + EMC_CLK_EMC_2X_CLK_SRC_SHIFT; + emc_clk_div = (clksrc & EMC_CLK_EMC_2X_CLK_DIVISOR_MASK) >> + EMC_CLK_EMC_2X_CLK_DIVISOR_SHIFT; + + dll_setting &= ~(DLL_CLK_EMC_DLL_CLK_SRC_MASK | + DLL_CLK_EMC_DLL_CLK_DIVISOR_MASK); + dll_setting |= emc_clk_src << DLL_CLK_EMC_DLL_CLK_SRC_SHIFT; + dll_setting |= emc_clk_div << DLL_CLK_EMC_DLL_CLK_DIVISOR_SHIFT; + + dll_setting &= ~DLL_CLK_EMC_DLL_DDLL_CLK_SEL_MASK; + if (emc_clk_src == EMC_CLK_SOURCE_PLLMB_LJ) + dll_setting |= (PLLM_VCOB << + DLL_CLK_EMC_DLL_DDLL_CLK_SEL_SHIFT); + else if (emc_clk_src == EMC_CLK_SOURCE_PLLM_LJ) + dll_setting |= (PLLM_VCOA << + DLL_CLK_EMC_DLL_DDLL_CLK_SEL_SHIFT); + else + dll_setting |= (EMC_DLL_SWITCH_OUT << + DLL_CLK_EMC_DLL_DDLL_CLK_SEL_SHIFT); + + tegra210_clk_emc_dll_update_setting(dll_setting); + + if (emc->next->clk_out_enb_x_0_clk_enb_emc_dll) + tegra210_clk_emc_dll_enable(true); + else + tegra210_clk_emc_dll_enable(false); +} + +int tegra210_emc_set_refresh(struct tegra210_emc *emc, + enum tegra210_emc_refresh refresh) +{ + struct tegra210_emc_timing *timings; + unsigned long flags; + + if ((emc->dram_type != DRAM_TYPE_LPDDR2 && + emc->dram_type != DRAM_TYPE_LPDDR4) || + !emc->last) + return -ENODEV; + + if (refresh > TEGRA210_EMC_REFRESH_THROTTLE) + return -EINVAL; + + if (refresh == emc->refresh) + return 0; + + spin_lock_irqsave(&emc->lock, flags); + + if (refresh == TEGRA210_EMC_REFRESH_THROTTLE && emc->derated) + timings = emc->derated; + else + timings = emc->nominal; + + if (timings != emc->timings) { + unsigned int index = emc->last - emc->timings; + u32 clksrc; + + clksrc = emc->provider.configs[index].value | + EMC_CLK_FORCE_CC_TRIGGER; + + emc->next = &timings[index]; + emc->timings = timings; + + tegra210_emc_set_clock(emc, clksrc); + } else { + tegra210_emc_adjust_timing(emc, emc->last); + tegra210_emc_timing_update(emc); + + if (refresh != TEGRA210_EMC_REFRESH_NOMINAL) + emc_writel(emc, EMC_REF_REF_CMD, EMC_REF); + } + + spin_unlock_irqrestore(&emc->lock, flags); + + return 0; +} + +u32 tegra210_emc_mrr_read(struct tegra210_emc *emc, unsigned int chip, + unsigned int address) +{ + u32 value, ret = 0; + unsigned int i; + + value = (chip & EMC_MRR_DEV_SEL_MASK) << EMC_MRR_DEV_SEL_SHIFT | + (address & EMC_MRR_MA_MASK) << EMC_MRR_MA_SHIFT; + emc_writel(emc, value, EMC_MRR); + + for (i = 0; i < emc->num_channels; i++) + WARN(tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, + EMC_EMC_STATUS_MRR_DIVLD, 1), + "Timed out waiting for MRR %u (ch=%u)\n", address, i); + + for (i = 0; i < emc->num_channels; i++) { + value = emc_channel_readl(emc, i, EMC_MRR); + value &= EMC_MRR_DATA_MASK; + + ret = (ret << 16) | value; + } + + return ret; +} + +void tegra210_emc_do_clock_change(struct tegra210_emc *emc, u32 clksrc) +{ + int err; + + mc_readl(emc->mc, MC_EMEM_ADR_CFG); + emc_readl(emc, EMC_INTSTATUS); + + tegra210_clk_emc_update_setting(clksrc); + + err = tegra210_emc_wait_for_update(emc, 0, EMC_INTSTATUS, + EMC_INTSTATUS_CLKCHANGE_COMPLETE, + true); + if (err) + dev_warn(emc->dev, "clock change completion error: %d\n", err); +} + +struct tegra210_emc_timing *tegra210_emc_find_timing(struct tegra210_emc *emc, + unsigned long rate) +{ + unsigned int i; + + for (i = 0; i < emc->num_timings; i++) + if (emc->timings[i].rate * 1000UL == rate) + return &emc->timings[i]; + + return NULL; +} + +int tegra210_emc_wait_for_update(struct tegra210_emc *emc, unsigned int channel, + unsigned int offset, u32 bit_mask, bool state) +{ + unsigned int i; + u32 value; + + for (i = 0; i < EMC_STATUS_UPDATE_TIMEOUT; i++) { + value = emc_channel_readl(emc, channel, offset); + if (!!(value & bit_mask) == state) + return 0; + + udelay(1); + } + + return -ETIMEDOUT; +} + +void tegra210_emc_set_shadow_bypass(struct tegra210_emc *emc, int set) +{ + u32 emc_dbg = emc_readl(emc, EMC_DBG); + + if (set) + emc_writel(emc, emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE, EMC_DBG); + else + emc_writel(emc, emc_dbg & ~EMC_DBG_WRITE_MUX_ACTIVE, EMC_DBG); +} + +u32 tegra210_emc_get_dll_state(struct tegra210_emc_timing *next) +{ + if (next->emc_emrs & 0x1) + return 0; + + return 1; +} + +void tegra210_emc_timing_update(struct tegra210_emc *emc) +{ + unsigned int i; + int err = 0; + + emc_writel(emc, 0x1, EMC_TIMING_CONTROL); + + for (i = 0; i < emc->num_channels; i++) { + err |= tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, + EMC_EMC_STATUS_TIMING_UPDATE_STALLED, + false); + } + + if (err) + dev_warn(emc->dev, "timing update error: %d\n", err); +} + +unsigned long tegra210_emc_actual_osc_clocks(u32 in) +{ + if (in < 0x40) + return in * 16; + else if (in < 0x80) + return 2048; + else if (in < 0xc0) + return 4096; + else + return 8192; +} + +void tegra210_emc_start_periodic_compensation(struct tegra210_emc *emc) +{ + u32 mpc_req = 0x4b; + + emc_writel(emc, mpc_req, EMC_MPC); + mpc_req = emc_readl(emc, EMC_MPC); +} + +u32 tegra210_emc_compensate(struct tegra210_emc_timing *next, u32 offset) +{ + u32 temp = 0, rate = next->rate / 1000; + s32 delta[4], delta_taps[4]; + s32 new[] = { + TRIM_REG(0, 0, 0, 0), + TRIM_REG(0, 0, 0, 1), + TRIM_REG(0, 0, 1, 2), + TRIM_REG(0, 0, 1, 3), + + TRIM_REG(1, 0, 2, 4), + TRIM_REG(1, 0, 2, 5), + TRIM_REG(1, 0, 3, 6), + TRIM_REG(1, 0, 3, 7), + + TRIM_REG(0, 1, 0, 0), + TRIM_REG(0, 1, 0, 1), + TRIM_REG(0, 1, 1, 2), + TRIM_REG(0, 1, 1, 3), + + TRIM_REG(1, 1, 2, 4), + TRIM_REG(1, 1, 2, 5), + TRIM_REG(1, 1, 3, 6), + TRIM_REG(1, 1, 3, 7) + }; + unsigned i; + + switch (offset) { + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0: + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1: + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2: + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3: + case EMC_DATA_BRLSHFT_0: + delta[0] = 128 * (next->current_dram_clktree[C0D0U0] - + next->trained_dram_clktree[C0D0U0]); + delta[1] = 128 * (next->current_dram_clktree[C0D0U1] - + next->trained_dram_clktree[C0D0U1]); + delta[2] = 128 * (next->current_dram_clktree[C1D0U0] - + next->trained_dram_clktree[C1D0U0]); + delta[3] = 128 * (next->current_dram_clktree[C1D0U1] - + next->trained_dram_clktree[C1D0U1]); + + delta_taps[0] = (delta[0] * (s32)rate) / 1000000; + delta_taps[1] = (delta[1] * (s32)rate) / 1000000; + delta_taps[2] = (delta[2] * (s32)rate) / 1000000; + delta_taps[3] = (delta[3] * (s32)rate) / 1000000; + + for (i = 0; i < 4; i++) { + if ((delta_taps[i] > next->tree_margin) || + (delta_taps[i] < (-1 * next->tree_margin))) { + new[i * 2] = new[i * 2] + delta_taps[i]; + new[i * 2 + 1] = new[i * 2 + 1] + + delta_taps[i]; + } + } + + if (offset == EMC_DATA_BRLSHFT_0) { + for (i = 0; i < 8; i++) + new[i] = new[i] / 64; + } else { + for (i = 0; i < 8; i++) + new[i] = new[i] % 64; + } + + break; + + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0: + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1: + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2: + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3: + case EMC_DATA_BRLSHFT_1: + delta[0] = 128 * (next->current_dram_clktree[C0D1U0] - + next->trained_dram_clktree[C0D1U0]); + delta[1] = 128 * (next->current_dram_clktree[C0D1U1] - + next->trained_dram_clktree[C0D1U1]); + delta[2] = 128 * (next->current_dram_clktree[C1D1U0] - + next->trained_dram_clktree[C1D1U0]); + delta[3] = 128 * (next->current_dram_clktree[C1D1U1] - + next->trained_dram_clktree[C1D1U1]); + + delta_taps[0] = (delta[0] * (s32)rate) / 1000000; + delta_taps[1] = (delta[1] * (s32)rate) / 1000000; + delta_taps[2] = (delta[2] * (s32)rate) / 1000000; + delta_taps[3] = (delta[3] * (s32)rate) / 1000000; + + for (i = 0; i < 4; i++) { + if ((delta_taps[i] > next->tree_margin) || + (delta_taps[i] < (-1 * next->tree_margin))) { + new[8 + i * 2] = new[8 + i * 2] + + delta_taps[i]; + new[8 + i * 2 + 1] = new[8 + i * 2 + 1] + + delta_taps[i]; + } + } + + if (offset == EMC_DATA_BRLSHFT_1) { + for (i = 0; i < 8; i++) + new[i + 8] = new[i + 8] / 64; + } else { + for (i = 0; i < 8; i++) + new[i + 8] = new[i + 8] % 64; + } + + break; + } + + switch (offset) { + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0: + temp = CALC_TEMP(0, 0, 0, 1, 0); + break; + + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1: + temp = CALC_TEMP(0, 1, 2, 3, 2); + break; + + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2: + temp = CALC_TEMP(0, 2, 4, 5, 4); + break; + + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3: + temp = CALC_TEMP(0, 3, 6, 7, 6); + break; + + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0: + temp = CALC_TEMP(1, 0, 0, 1, 8); + break; + + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1: + temp = CALC_TEMP(1, 1, 2, 3, 10); + break; + + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2: + temp = CALC_TEMP(1, 2, 4, 5, 12); + break; + + case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3: + temp = CALC_TEMP(1, 3, 6, 7, 14); + break; + + case EMC_DATA_BRLSHFT_0: + temp = ((new[0] << + EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_MASK) | + ((new[1] << + EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_MASK) | + ((new[2] << + EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_MASK) | + ((new[3] << + EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_MASK) | + ((new[4] << + EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_MASK) | + ((new[5] << + EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_MASK) | + ((new[6] << + EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_MASK) | + ((new[7] << + EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_MASK); + break; + + case EMC_DATA_BRLSHFT_1: + temp = ((new[8] << + EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_MASK) | + ((new[9] << + EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_MASK) | + ((new[10] << + EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_MASK) | + ((new[11] << + EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_MASK) | + ((new[12] << + EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_MASK) | + ((new[13] << + EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_MASK) | + ((new[14] << + EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_MASK) | + ((new[15] << + EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_SHIFT) & + EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_MASK); + break; + + default: + break; + } + + return temp; +} + +u32 tegra210_emc_dll_prelock(struct tegra210_emc *emc, u32 clksrc) +{ + unsigned int i; + u32 value; + + value = emc_readl(emc, EMC_CFG_DIG_DLL); + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_MASK; + value |= (3 << EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_SHIFT); + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN; + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK; + value |= (3 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT); + value |= EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC; + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK; + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK; + emc_writel(emc, value, EMC_CFG_DIG_DLL); + emc_writel(emc, 1, EMC_TIMING_CONTROL); + + for (i = 0; i < emc->num_channels; i++) + tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, + EMC_EMC_STATUS_TIMING_UPDATE_STALLED, + 0); + + for (i = 0; i < emc->num_channels; i++) { + while (true) { + value = emc_channel_readl(emc, i, EMC_CFG_DIG_DLL); + if ((value & EMC_CFG_DIG_DLL_CFG_DLL_EN) == 0) + break; + } + } + + value = emc->next->burst_regs[EMC_DLL_CFG_0_INDEX]; + emc_writel(emc, value, EMC_DLL_CFG_0); + + value = emc_readl(emc, EMC_DLL_CFG_1); + value &= EMC_DLL_CFG_1_DDLLCAL_CTRL_START_TRIM_MASK; + + if (emc->next->rate >= 400000 && emc->next->rate < 600000) + value |= 150; + else if (emc->next->rate >= 600000 && emc->next->rate < 800000) + value |= 100; + else if (emc->next->rate >= 800000 && emc->next->rate < 1000000) + value |= 70; + else if (emc->next->rate >= 1000000 && emc->next->rate < 1200000) + value |= 30; + else + value |= 20; + + emc_writel(emc, value, EMC_DLL_CFG_1); + + tegra210_change_dll_src(emc, clksrc); + + value = emc_readl(emc, EMC_CFG_DIG_DLL); + value |= EMC_CFG_DIG_DLL_CFG_DLL_EN; + emc_writel(emc, value, EMC_CFG_DIG_DLL); + + tegra210_emc_timing_update(emc); + + for (i = 0; i < emc->num_channels; i++) { + while (true) { + value = emc_channel_readl(emc, 0, EMC_CFG_DIG_DLL); + if (value & EMC_CFG_DIG_DLL_CFG_DLL_EN) + break; + } + } + + while (true) { + value = emc_readl(emc, EMC_DIG_DLL_STATUS); + + if ((value & EMC_DIG_DLL_STATUS_DLL_PRIV_UPDATED) == 0) + continue; + + if ((value & EMC_DIG_DLL_STATUS_DLL_LOCK) == 0) + continue; + + break; + } + + value = emc_readl(emc, EMC_DIG_DLL_STATUS); + + return value & EMC_DIG_DLL_STATUS_DLL_OUT_MASK; +} + +u32 tegra210_emc_dvfs_power_ramp_up(struct tegra210_emc *emc, u32 clk, + bool flip_backward) +{ + u32 cmd_pad, dq_pad, rfu1, cfg5, common_tx, ramp_up_wait = 0; + const struct tegra210_emc_timing *timing; + + if (flip_backward) + timing = emc->last; + else + timing = emc->next; + + cmd_pad = timing->burst_regs[EMC_PMACRO_CMD_PAD_TX_CTRL_INDEX]; + dq_pad = timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX]; + rfu1 = timing->burst_regs[EMC_PMACRO_BRICK_CTRL_RFU1_INDEX]; + cfg5 = timing->burst_regs[EMC_FBIO_CFG5_INDEX]; + common_tx = timing->burst_regs[EMC_PMACRO_COMMON_PAD_TX_CTRL_INDEX]; + + cmd_pad |= EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON; + + if (clk < 1000000 / DVFS_FGCG_MID_SPEED_THRESHOLD) { + ccfifo_writel(emc, common_tx & 0xa, + EMC_PMACRO_COMMON_PAD_TX_CTRL, 0); + ccfifo_writel(emc, common_tx & 0xf, + EMC_PMACRO_COMMON_PAD_TX_CTRL, + (100000 / clk) + 1); + ramp_up_wait += 100000; + } else { + ccfifo_writel(emc, common_tx | 0x8, + EMC_PMACRO_COMMON_PAD_TX_CTRL, 0); + } + + if (clk < 1000000 / DVFS_FGCG_HIGH_SPEED_THRESHOLD) { + if (clk < 1000000 / IOBRICK_DCC_THRESHOLD) { + cmd_pad |= + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC; + cmd_pad &= + ~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC); + ccfifo_writel(emc, cmd_pad, + EMC_PMACRO_CMD_PAD_TX_CTRL, + (100000 / clk) + 1); + ramp_up_wait += 100000; + + dq_pad |= + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC; + dq_pad &= + ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC); + ccfifo_writel(emc, dq_pad, + EMC_PMACRO_DATA_PAD_TX_CTRL, 0); + ccfifo_writel(emc, rfu1 & 0xfe40fe40, + EMC_PMACRO_BRICK_CTRL_RFU1, 0); + } else { + ccfifo_writel(emc, rfu1 & 0xfe40fe40, + EMC_PMACRO_BRICK_CTRL_RFU1, + (100000 / clk) + 1); + ramp_up_wait += 100000; + } + + ccfifo_writel(emc, rfu1 & 0xfeedfeed, + EMC_PMACRO_BRICK_CTRL_RFU1, (100000 / clk) + 1); + ramp_up_wait += 100000; + + if (clk < 1000000 / IOBRICK_DCC_THRESHOLD) { + cmd_pad |= + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC; + ccfifo_writel(emc, cmd_pad, + EMC_PMACRO_CMD_PAD_TX_CTRL, + (100000 / clk) + 1); + ramp_up_wait += 100000; + + dq_pad |= + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC; + ccfifo_writel(emc, dq_pad, + EMC_PMACRO_DATA_PAD_TX_CTRL, 0); + ccfifo_writel(emc, rfu1, + EMC_PMACRO_BRICK_CTRL_RFU1, 0); + } else { + ccfifo_writel(emc, rfu1, + EMC_PMACRO_BRICK_CTRL_RFU1, + (100000 / clk) + 1); + ramp_up_wait += 100000; + } + + ccfifo_writel(emc, cfg5 & ~EMC_FBIO_CFG5_CMD_TX_DIS, + EMC_FBIO_CFG5, (100000 / clk) + 10); + ramp_up_wait += 100000 + (10 * clk); + } else if (clk < 1000000 / DVFS_FGCG_MID_SPEED_THRESHOLD) { + ccfifo_writel(emc, rfu1 | 0x06000600, + EMC_PMACRO_BRICK_CTRL_RFU1, (100000 / clk) + 1); + ccfifo_writel(emc, cfg5 & ~EMC_FBIO_CFG5_CMD_TX_DIS, + EMC_FBIO_CFG5, (100000 / clk) + 10); + ramp_up_wait += 100000 + 10 * clk; + } else { + ccfifo_writel(emc, rfu1 | 0x00000600, + EMC_PMACRO_BRICK_CTRL_RFU1, 0); + ccfifo_writel(emc, cfg5 & ~EMC_FBIO_CFG5_CMD_TX_DIS, + EMC_FBIO_CFG5, 12); + ramp_up_wait += 12 * clk; + } + + cmd_pad &= ~EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON; + ccfifo_writel(emc, cmd_pad, EMC_PMACRO_CMD_PAD_TX_CTRL, 5); + + return ramp_up_wait; +} + +u32 tegra210_emc_dvfs_power_ramp_down(struct tegra210_emc *emc, u32 clk, + bool flip_backward) +{ + u32 ramp_down_wait = 0, cmd_pad, dq_pad, rfu1, cfg5, common_tx; + const struct tegra210_emc_timing *entry; + u32 seq_wait; + + if (flip_backward) + entry = emc->next; + else + entry = emc->last; + + cmd_pad = entry->burst_regs[EMC_PMACRO_CMD_PAD_TX_CTRL_INDEX]; + dq_pad = entry->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX]; + rfu1 = entry->burst_regs[EMC_PMACRO_BRICK_CTRL_RFU1_INDEX]; + cfg5 = entry->burst_regs[EMC_FBIO_CFG5_INDEX]; + common_tx = entry->burst_regs[EMC_PMACRO_COMMON_PAD_TX_CTRL_INDEX]; + + cmd_pad |= EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON; + + ccfifo_writel(emc, cmd_pad, EMC_PMACRO_CMD_PAD_TX_CTRL, 0); + ccfifo_writel(emc, cfg5 | EMC_FBIO_CFG5_CMD_TX_DIS, + EMC_FBIO_CFG5, 12); + ramp_down_wait = 12 * clk; + + seq_wait = (100000 / clk) + 1; + + if (clk < (1000000 / DVFS_FGCG_HIGH_SPEED_THRESHOLD)) { + if (clk < (1000000 / IOBRICK_DCC_THRESHOLD)) { + cmd_pad &= + ~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC); + cmd_pad |= + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC; + ccfifo_writel(emc, cmd_pad, + EMC_PMACRO_CMD_PAD_TX_CTRL, seq_wait); + ramp_down_wait += 100000; + + dq_pad &= + ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC); + dq_pad |= + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC; + ccfifo_writel(emc, dq_pad, + EMC_PMACRO_DATA_PAD_TX_CTRL, 0); + ccfifo_writel(emc, rfu1 & ~0x01120112, + EMC_PMACRO_BRICK_CTRL_RFU1, 0); + } else { + ccfifo_writel(emc, rfu1 & ~0x01120112, + EMC_PMACRO_BRICK_CTRL_RFU1, seq_wait); + ramp_down_wait += 100000; + } + + ccfifo_writel(emc, rfu1 & ~0x01bf01bf, + EMC_PMACRO_BRICK_CTRL_RFU1, seq_wait); + ramp_down_wait += 100000; + + if (clk < (1000000 / IOBRICK_DCC_THRESHOLD)) { + cmd_pad &= + ~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC | + EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC); + ccfifo_writel(emc, cmd_pad, + EMC_PMACRO_CMD_PAD_TX_CTRL, seq_wait); + ramp_down_wait += 100000; + + dq_pad &= + ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC | + EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC); + ccfifo_writel(emc, dq_pad, + EMC_PMACRO_DATA_PAD_TX_CTRL, 0); + ccfifo_writel(emc, rfu1 & ~0x07ff07ff, + EMC_PMACRO_BRICK_CTRL_RFU1, 0); + } else { + ccfifo_writel(emc, rfu1 & ~0x07ff07ff, + EMC_PMACRO_BRICK_CTRL_RFU1, seq_wait); + ramp_down_wait += 100000; + } + } else { + ccfifo_writel(emc, rfu1 & ~0xffff07ff, + EMC_PMACRO_BRICK_CTRL_RFU1, seq_wait + 19); + ramp_down_wait += 100000 + (20 * clk); + } + + if (clk < (1000000 / DVFS_FGCG_MID_SPEED_THRESHOLD)) { + ramp_down_wait += 100000; + ccfifo_writel(emc, common_tx & ~0x5, + EMC_PMACRO_COMMON_PAD_TX_CTRL, seq_wait); + ramp_down_wait += 100000; + ccfifo_writel(emc, common_tx & ~0xf, + EMC_PMACRO_COMMON_PAD_TX_CTRL, seq_wait); + ramp_down_wait += 100000; + ccfifo_writel(emc, 0, 0, seq_wait); + ramp_down_wait += 100000; + } else { + ccfifo_writel(emc, common_tx & ~0xf, + EMC_PMACRO_COMMON_PAD_TX_CTRL, seq_wait); + } + + return ramp_down_wait; +} + +void tegra210_emc_reset_dram_clktree_values(struct tegra210_emc_timing *timing) +{ + timing->current_dram_clktree[C0D0U0] = + timing->trained_dram_clktree[C0D0U0]; + timing->current_dram_clktree[C0D0U1] = + timing->trained_dram_clktree[C0D0U1]; + timing->current_dram_clktree[C1D0U0] = + timing->trained_dram_clktree[C1D0U0]; + timing->current_dram_clktree[C1D0U1] = + timing->trained_dram_clktree[C1D0U1]; + timing->current_dram_clktree[C1D1U0] = + timing->trained_dram_clktree[C1D1U0]; + timing->current_dram_clktree[C1D1U1] = + timing->trained_dram_clktree[C1D1U1]; +} + +static void update_dll_control(struct tegra210_emc *emc, u32 value, bool state) +{ + unsigned int i; + + emc_writel(emc, value, EMC_CFG_DIG_DLL); + tegra210_emc_timing_update(emc); + + for (i = 0; i < emc->num_channels; i++) + tegra210_emc_wait_for_update(emc, i, EMC_CFG_DIG_DLL, + EMC_CFG_DIG_DLL_CFG_DLL_EN, + state); +} + +void tegra210_emc_dll_disable(struct tegra210_emc *emc) +{ + u32 value; + + value = emc_readl(emc, EMC_CFG_DIG_DLL); + value &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN; + + update_dll_control(emc, value, false); +} + +void tegra210_emc_dll_enable(struct tegra210_emc *emc) +{ + u32 value; + + value = emc_readl(emc, EMC_CFG_DIG_DLL); + value |= EMC_CFG_DIG_DLL_CFG_DLL_EN; + + update_dll_control(emc, value, true); +} + +void tegra210_emc_adjust_timing(struct tegra210_emc *emc, + struct tegra210_emc_timing *timing) +{ + u32 dsr_cntrl = timing->burst_regs[EMC_DYN_SELF_REF_CONTROL_INDEX]; + u32 pre_ref = timing->burst_regs[EMC_PRE_REFRESH_REQ_CNT_INDEX]; + u32 ref = timing->burst_regs[EMC_REFRESH_INDEX]; + + switch (emc->refresh) { + case TEGRA210_EMC_REFRESH_NOMINAL: + case TEGRA210_EMC_REFRESH_THROTTLE: + break; + + case TEGRA210_EMC_REFRESH_2X: + ref = REFRESH_SPEEDUP(ref, 2); + pre_ref = REFRESH_SPEEDUP(pre_ref, 2); + dsr_cntrl = REFRESH_SPEEDUP(dsr_cntrl, 2); + break; + + case TEGRA210_EMC_REFRESH_4X: + ref = REFRESH_SPEEDUP(ref, 4); + pre_ref = REFRESH_SPEEDUP(pre_ref, 4); + dsr_cntrl = REFRESH_SPEEDUP(dsr_cntrl, 4); + break; + + default: + dev_warn(emc->dev, "failed to set refresh: %d\n", emc->refresh); + return; + } + + emc_writel(emc, ref, emc->offsets->burst[EMC_REFRESH_INDEX]); + emc_writel(emc, pre_ref, + emc->offsets->burst[EMC_PRE_REFRESH_REQ_CNT_INDEX]); + emc_writel(emc, dsr_cntrl, + emc->offsets->burst[EMC_DYN_SELF_REF_CONTROL_INDEX]); +} + +static int tegra210_emc_set_rate(struct device *dev, + const struct tegra210_clk_emc_config *config) +{ + struct tegra210_emc *emc = dev_get_drvdata(dev); + struct tegra210_emc_timing *timing = NULL; + unsigned long rate = config->rate; + s64 last_change_delay; + unsigned long flags; + unsigned int i; + + if (rate == emc->last->rate * 1000UL) + return 0; + + for (i = 0; i < emc->num_timings; i++) { + if (emc->timings[i].rate * 1000UL == rate) { + timing = &emc->timings[i]; + break; + } + } + + if (!timing) + return -EINVAL; + + if (rate > 204000000 && !timing->trained) + return -EINVAL; + + emc->next = timing; + last_change_delay = ktime_us_delta(ktime_get(), emc->clkchange_time); + + /* XXX use non-busy-looping sleep? */ + if ((last_change_delay >= 0) && + (last_change_delay < emc->clkchange_delay)) + udelay(emc->clkchange_delay - (int)last_change_delay); + + spin_lock_irqsave(&emc->lock, flags); + tegra210_emc_set_clock(emc, config->value); + emc->clkchange_time = ktime_get(); + emc->last = timing; + spin_unlock_irqrestore(&emc->lock, flags); + + return 0; +} + +/* + * debugfs interface + * + * The memory controller driver exposes some files in debugfs that can be used + * to control the EMC frequency. The top-level directory can be found here: + * + * /sys/kernel/debug/emc + * + * It contains the following files: + * + * - available_rates: This file contains a list of valid, space-separated + * EMC frequencies. + * + * - min_rate: Writing a value to this file sets the given frequency as the + * floor of the permitted range. If this is higher than the currently + * configured EMC frequency, this will cause the frequency to be + * increased so that it stays within the valid range. + * + * - max_rate: Similarily to the min_rate file, writing a value to this file + * sets the given frequency as the ceiling of the permitted range. If + * the value is lower than the currently configured EMC frequency, this + * will cause the frequency to be decreased so that it stays within the + * valid range. + */ + +static bool tegra210_emc_validate_rate(struct tegra210_emc *emc, + unsigned long rate) +{ + unsigned int i; + + for (i = 0; i < emc->num_timings; i++) + if (rate == emc->timings[i].rate * 1000UL) + return true; + + return false; +} + +static int tegra210_emc_debug_available_rates_show(struct seq_file *s, + void *data) +{ + struct tegra210_emc *emc = s->private; + const char *prefix = ""; + unsigned int i; + + for (i = 0; i < emc->num_timings; i++) { + seq_printf(s, "%s%u", prefix, emc->timings[i].rate * 1000); + prefix = " "; + } + + seq_puts(s, "\n"); + + return 0; +} + +static int tegra210_emc_debug_available_rates_open(struct inode *inode, + struct file *file) +{ + return single_open(file, tegra210_emc_debug_available_rates_show, + inode->i_private); +} + +static const struct file_operations tegra210_emc_debug_available_rates_fops = { + .open = tegra210_emc_debug_available_rates_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int tegra210_emc_debug_min_rate_get(void *data, u64 *rate) +{ + struct tegra210_emc *emc = data; + + *rate = emc->debugfs.min_rate; + + return 0; +} + +static int tegra210_emc_debug_min_rate_set(void *data, u64 rate) +{ + struct tegra210_emc *emc = data; + int err; + + if (!tegra210_emc_validate_rate(emc, rate)) + return -EINVAL; + + err = clk_set_min_rate(emc->clk, rate); + if (err < 0) + return err; + + emc->debugfs.min_rate = rate; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(tegra210_emc_debug_min_rate_fops, + tegra210_emc_debug_min_rate_get, + tegra210_emc_debug_min_rate_set, "%llu\n"); + +static int tegra210_emc_debug_max_rate_get(void *data, u64 *rate) +{ + struct tegra210_emc *emc = data; + + *rate = emc->debugfs.max_rate; + + return 0; +} + +static int tegra210_emc_debug_max_rate_set(void *data, u64 rate) +{ + struct tegra210_emc *emc = data; + int err; + + if (!tegra210_emc_validate_rate(emc, rate)) + return -EINVAL; + + err = clk_set_max_rate(emc->clk, rate); + if (err < 0) + return err; + + emc->debugfs.max_rate = rate; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(tegra210_emc_debug_max_rate_fops, + tegra210_emc_debug_max_rate_get, + tegra210_emc_debug_max_rate_set, "%llu\n"); + +static int tegra210_emc_debug_temperature_get(void *data, u64 *temperature) +{ + struct tegra210_emc *emc = data; + unsigned int value; + + if (!emc->debugfs.temperature) + value = tegra210_emc_get_temperature(emc); + else + value = emc->debugfs.temperature; + + *temperature = value; + + return 0; +} + +static int tegra210_emc_debug_temperature_set(void *data, u64 temperature) +{ + struct tegra210_emc *emc = data; + + if (temperature > 7) + return -EINVAL; + + emc->debugfs.temperature = temperature; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(tegra210_emc_debug_temperature_fops, + tegra210_emc_debug_temperature_get, + tegra210_emc_debug_temperature_set, "%llu\n"); + +static void tegra210_emc_debugfs_init(struct tegra210_emc *emc) +{ + struct device *dev = emc->dev; + unsigned int i; + int err; + + emc->debugfs.min_rate = ULONG_MAX; + emc->debugfs.max_rate = 0; + + for (i = 0; i < emc->num_timings; i++) { + if (emc->timings[i].rate * 1000UL < emc->debugfs.min_rate) + emc->debugfs.min_rate = emc->timings[i].rate * 1000UL; + + if (emc->timings[i].rate * 1000UL > emc->debugfs.max_rate) + emc->debugfs.max_rate = emc->timings[i].rate * 1000UL; + } + + if (!emc->num_timings) { + emc->debugfs.min_rate = clk_get_rate(emc->clk); + emc->debugfs.max_rate = emc->debugfs.min_rate; + } + + err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate, + emc->debugfs.max_rate); + if (err < 0) { + dev_err(dev, "failed to set rate range [%lu-%lu] for %pC\n", + emc->debugfs.min_rate, emc->debugfs.max_rate, + emc->clk); + return; + } + + emc->debugfs.root = debugfs_create_dir("emc", NULL); + if (!emc->debugfs.root) { + dev_err(dev, "failed to create debugfs directory\n"); + return; + } + + debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc, + &tegra210_emc_debug_available_rates_fops); + debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc, + &tegra210_emc_debug_min_rate_fops); + debugfs_create_file("max_rate", 0644, emc->debugfs.root, emc, + &tegra210_emc_debug_max_rate_fops); + debugfs_create_file("temperature", 0644, emc->debugfs.root, emc, + &tegra210_emc_debug_temperature_fops); +} + +static void tegra210_emc_detect(struct tegra210_emc *emc) +{ + u32 value; + + /* probe the number of connected DRAM devices */ + value = mc_readl(emc->mc, MC_EMEM_ADR_CFG); + + if (value & MC_EMEM_ADR_CFG_EMEM_NUMDEV) + emc->num_devices = 2; + else + emc->num_devices = 1; + + /* probe the type of DRAM */ + value = emc_readl(emc, EMC_FBIO_CFG5); + emc->dram_type = value & 0x3; + + /* probe the number of channels */ + value = emc_readl(emc, EMC_FBIO_CFG7); + + if ((value & EMC_FBIO_CFG7_CH1_ENABLE) && + (value & EMC_FBIO_CFG7_CH0_ENABLE)) + emc->num_channels = 2; + else + emc->num_channels = 1; +} + +static int tegra210_emc_validate_timings(struct tegra210_emc *emc, + struct tegra210_emc_timing *timings, + unsigned int num_timings) +{ + unsigned int i; + + for (i = 0; i < num_timings; i++) { + u32 min_volt = timings[i].min_volt; + u32 rate = timings[i].rate; + + if (!rate) + return -EINVAL; + + if ((i > 0) && ((rate <= timings[i - 1].rate) || + (min_volt < timings[i - 1].min_volt))) + return -EINVAL; + + if (timings[i].revision != timings[0].revision) + continue; + } + + return 0; +} + +static int tegra210_emc_probe(struct platform_device *pdev) +{ + struct thermal_cooling_device *cd; + unsigned long current_rate; + struct platform_device *mc; + struct tegra210_emc *emc; + struct device_node *np; + unsigned int i; + int err; + + emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL); + if (!emc) + return -ENOMEM; + + emc->clk = devm_clk_get(&pdev->dev, "emc"); + if (IS_ERR(emc->clk)) + return PTR_ERR(emc->clk); + + platform_set_drvdata(pdev, emc); + spin_lock_init(&emc->lock); + emc->dev = &pdev->dev; + + np = of_parse_phandle(pdev->dev.of_node, "nvidia,memory-controller", 0); + if (!np) { + dev_err(&pdev->dev, "could not get memory controller\n"); + return -ENOENT; + } + + mc = of_find_device_by_node(np); + of_node_put(np); + if (!mc) + return -ENOENT; + + emc->mc = platform_get_drvdata(mc); + if (!emc->mc) { + put_device(&mc->dev); + return -EPROBE_DEFER; + } + + emc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(emc->regs)) { + err = PTR_ERR(emc->regs); + goto put_mc; + } + + for (i = 0; i < 2; i++) { + emc->channel[i] = devm_platform_ioremap_resource(pdev, 1 + i); + if (IS_ERR(emc->channel[i])) { + err = PTR_ERR(emc->channel[i]); + goto put_mc; + } + } + + tegra210_emc_detect(emc); + np = pdev->dev.of_node; + + /* attach to the nominal and (optional) derated tables */ + err = of_reserved_mem_device_init_by_name(emc->dev, np, "nominal"); + if (err < 0) { + dev_err(emc->dev, "failed to get nominal EMC table: %d\n", err); + goto put_mc; + } + + err = of_reserved_mem_device_init_by_name(emc->dev, np, "derated"); + if (err < 0 && err != -ENODEV) { + dev_err(emc->dev, "failed to get derated EMC table: %d\n", err); + goto release; + } + + /* validate the tables */ + if (emc->nominal) { + err = tegra210_emc_validate_timings(emc, emc->nominal, + emc->num_timings); + if (err < 0) + goto release; + } + + if (emc->derated) { + err = tegra210_emc_validate_timings(emc, emc->derated, + emc->num_timings); + if (err < 0) + goto release; + } + + /* default to the nominal table */ + emc->timings = emc->nominal; + + /* pick the current timing based on the current EMC clock rate */ + current_rate = clk_get_rate(emc->clk) / 1000; + + for (i = 0; i < emc->num_timings; i++) { + if (emc->timings[i].rate == current_rate) { + emc->last = &emc->timings[i]; + break; + } + } + + if (i == emc->num_timings) { + dev_err(emc->dev, "no EMC table entry found for %lu kHz\n", + current_rate); + err = -ENOENT; + goto release; + } + + /* pick a compatible clock change sequence for the EMC table */ + for (i = 0; i < ARRAY_SIZE(tegra210_emc_sequences); i++) { + const struct tegra210_emc_sequence *sequence = + tegra210_emc_sequences[i]; + + if (emc->timings[0].revision == sequence->revision) { + emc->sequence = sequence; + break; + } + } + + if (!emc->sequence) { + dev_err(&pdev->dev, "sequence %u not supported\n", + emc->timings[0].revision); + err = -ENOTSUPP; + goto release; + } + + emc->offsets = &tegra210_emc_table_register_offsets; + emc->refresh = TEGRA210_EMC_REFRESH_NOMINAL; + + emc->provider.owner = THIS_MODULE; + emc->provider.dev = &pdev->dev; + emc->provider.set_rate = tegra210_emc_set_rate; + + emc->provider.configs = devm_kcalloc(&pdev->dev, emc->num_timings, + sizeof(*emc->provider.configs), + GFP_KERNEL); + if (!emc->provider.configs) { + err = -ENOMEM; + goto release; + } + + emc->provider.num_configs = emc->num_timings; + + for (i = 0; i < emc->provider.num_configs; i++) { + struct tegra210_emc_timing *timing = &emc->timings[i]; + struct tegra210_clk_emc_config *config = + &emc->provider.configs[i]; + u32 value; + + config->rate = timing->rate * 1000UL; + config->value = timing->clk_src_emc; + + value = timing->burst_mc_regs[MC_EMEM_ARB_MISC0_INDEX]; + + if ((value & MC_EMEM_ARB_MISC0_EMC_SAME_FREQ) == 0) + config->same_freq = false; + else + config->same_freq = true; + } + + err = tegra210_clk_emc_attach(emc->clk, &emc->provider); + if (err < 0) { + dev_err(&pdev->dev, "failed to attach to EMC clock: %d\n", err); + goto release; + } + + emc->clkchange_delay = 100; + emc->training_interval = 100; + dev_set_drvdata(emc->dev, emc); + + timer_setup(&emc->refresh_timer, tegra210_emc_poll_refresh, + TIMER_DEFERRABLE); + atomic_set(&emc->refresh_poll, 0); + emc->refresh_poll_interval = 1000; + + timer_setup(&emc->training, tegra210_emc_train, 0); + + tegra210_emc_debugfs_init(emc); + + cd = devm_thermal_of_cooling_device_register(emc->dev, np, "emc", emc, + &tegra210_emc_cd_ops); + if (IS_ERR(cd)) { + err = PTR_ERR(cd); + dev_err(emc->dev, "failed to register cooling device: %d\n", + err); + goto detach; + } + + return 0; + +detach: + debugfs_remove_recursive(emc->debugfs.root); + tegra210_clk_emc_detach(emc->clk); +release: + of_reserved_mem_device_release(emc->dev); +put_mc: + put_device(emc->mc->dev); + return err; +} + +static int tegra210_emc_remove(struct platform_device *pdev) +{ + struct tegra210_emc *emc = platform_get_drvdata(pdev); + + debugfs_remove_recursive(emc->debugfs.root); + tegra210_clk_emc_detach(emc->clk); + of_reserved_mem_device_release(emc->dev); + put_device(emc->mc->dev); + + return 0; +} + +static int __maybe_unused tegra210_emc_suspend(struct device *dev) +{ + struct tegra210_emc *emc = dev_get_drvdata(dev); + int err; + + err = clk_rate_exclusive_get(emc->clk); + if (err < 0) { + dev_err(emc->dev, "failed to acquire clock: %d\n", err); + return err; + } + + emc->resume_rate = clk_get_rate(emc->clk); + + clk_set_rate(emc->clk, 204000000); + tegra210_clk_emc_detach(emc->clk); + + dev_dbg(dev, "suspending at %lu Hz\n", clk_get_rate(emc->clk)); + + return 0; +} + +static int __maybe_unused tegra210_emc_resume(struct device *dev) +{ + struct tegra210_emc *emc = dev_get_drvdata(dev); + int err; + + err = tegra210_clk_emc_attach(emc->clk, &emc->provider); + if (err < 0) { + dev_err(dev, "failed to attach to EMC clock: %d\n", err); + return err; + } + + clk_set_rate(emc->clk, emc->resume_rate); + clk_rate_exclusive_put(emc->clk); + + dev_dbg(dev, "resuming at %lu Hz\n", clk_get_rate(emc->clk)); + + return 0; +} + +static const struct dev_pm_ops tegra210_emc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra210_emc_suspend, tegra210_emc_resume) +}; + +static const struct of_device_id tegra210_emc_of_match[] = { + { .compatible = "nvidia,tegra210-emc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra210_emc_of_match); + +static struct platform_driver tegra210_emc_driver = { + .driver = { + .name = "tegra210-emc", + .of_match_table = tegra210_emc_of_match, + .pm = &tegra210_emc_pm_ops, + }, + .probe = tegra210_emc_probe, + .remove = tegra210_emc_remove, +}; + +module_platform_driver(tegra210_emc_driver); + +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_AUTHOR("Joseph Lo <josephl@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra210 EMC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/memory/tegra/tegra210-emc-table.c b/drivers/memory/tegra/tegra210-emc-table.c new file mode 100644 index 000000000000..3e0598363b87 --- /dev/null +++ b/drivers/memory/tegra/tegra210-emc-table.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + */ + +#include <linux/of_reserved_mem.h> + +#include "tegra210-emc.h" + +#define TEGRA_EMC_MAX_FREQS 16 + +static int tegra210_emc_table_device_init(struct reserved_mem *rmem, + struct device *dev) +{ + struct tegra210_emc *emc = dev_get_drvdata(dev); + struct tegra210_emc_timing *timings; + unsigned int i, count = 0; + + timings = memremap(rmem->base, rmem->size, MEMREMAP_WB); + if (!timings) { + dev_err(dev, "failed to map EMC table\n"); + return -ENOMEM; + } + + count = 0; + + for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) { + if (timings[i].revision == 0) + break; + + count++; + } + + /* only the nominal and derated tables are expected */ + if (emc->derated) { + dev_warn(dev, "excess EMC table '%s'\n", rmem->name); + goto out; + } + + if (emc->nominal) { + if (count != emc->num_timings) { + dev_warn(dev, "%u derated vs. %u nominal entries\n", + count, emc->num_timings); + memunmap(timings); + return -EINVAL; + } + + emc->derated = timings; + } else { + emc->num_timings = count; + emc->nominal = timings; + } + +out: + /* keep track of which table this is */ + rmem->priv = timings; + + return 0; +} + +static void tegra210_emc_table_device_release(struct reserved_mem *rmem, + struct device *dev) +{ + struct tegra210_emc_timing *timings = rmem->priv; + struct tegra210_emc *emc = dev_get_drvdata(dev); + + if ((emc->nominal && timings != emc->nominal) && + (emc->derated && timings != emc->derated)) + dev_warn(dev, "trying to release unassigned EMC table '%s'\n", + rmem->name); + + memunmap(timings); +} + +static const struct reserved_mem_ops tegra210_emc_table_ops = { + .device_init = tegra210_emc_table_device_init, + .device_release = tegra210_emc_table_device_release, +}; + +static int tegra210_emc_table_init(struct reserved_mem *rmem) +{ + pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base, + (unsigned long)rmem->size); + + rmem->ops = &tegra210_emc_table_ops; + + return 0; +} +RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table", + tegra210_emc_table_init); diff --git a/drivers/memory/tegra/tegra210-emc.h b/drivers/memory/tegra/tegra210-emc.h new file mode 100644 index 000000000000..8988bcf15290 --- /dev/null +++ b/drivers/memory/tegra/tegra210-emc.h @@ -0,0 +1,1016 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved. + */ + +#ifndef TEGRA210_EMC_H +#define TEGRA210_EMC_H + +#include <linux/clk.h> +#include <linux/clk/tegra.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#define DVFS_FGCG_HIGH_SPEED_THRESHOLD 1000 +#define IOBRICK_DCC_THRESHOLD 2400 +#define DVFS_FGCG_MID_SPEED_THRESHOLD 600 + +#define EMC_STATUS_UPDATE_TIMEOUT 1000 + +/* register definitions */ +#define EMC_INTSTATUS 0x0 +#define EMC_INTSTATUS_CLKCHANGE_COMPLETE BIT(4) +#define EMC_DBG 0x8 +#define EMC_DBG_WRITE_MUX_ACTIVE BIT(1) +#define EMC_DBG_WRITE_ACTIVE_ONLY BIT(30) +#define EMC_CFG 0xc +#define EMC_CFG_DRAM_CLKSTOP_PD BIT(31) +#define EMC_CFG_DRAM_CLKSTOP_SR BIT(30) +#define EMC_CFG_DRAM_ACPD BIT(29) +#define EMC_CFG_DYN_SELF_REF BIT(28) +#define EMC_PIN 0x24 +#define EMC_PIN_PIN_CKE BIT(0) +#define EMC_PIN_PIN_CKEB BIT(1) +#define EMC_PIN_PIN_CKE_PER_DEV BIT(2) +#define EMC_TIMING_CONTROL 0x28 +#define EMC_RC 0x2c +#define EMC_RFC 0x30 +#define EMC_RAS 0x34 +#define EMC_RP 0x38 +#define EMC_R2W 0x3c +#define EMC_W2R 0x40 +#define EMC_R2P 0x44 +#define EMC_W2P 0x48 +#define EMC_RD_RCD 0x4c +#define EMC_WR_RCD 0x50 +#define EMC_RRD 0x54 +#define EMC_REXT 0x58 +#define EMC_WDV 0x5c +#define EMC_QUSE 0x60 +#define EMC_QRST 0x64 +#define EMC_QSAFE 0x68 +#define EMC_RDV 0x6c +#define EMC_REFRESH 0x70 +#define EMC_BURST_REFRESH_NUM 0x74 +#define EMC_PDEX2WR 0x78 +#define EMC_PDEX2RD 0x7c +#define EMC_PCHG2PDEN 0x80 +#define EMC_ACT2PDEN 0x84 +#define EMC_AR2PDEN 0x88 +#define EMC_RW2PDEN 0x8c +#define EMC_TXSR 0x90 +#define EMC_TCKE 0x94 +#define EMC_TFAW 0x98 +#define EMC_TRPAB 0x9c +#define EMC_TCLKSTABLE 0xa0 +#define EMC_TCLKSTOP 0xa4 +#define EMC_TREFBW 0xa8 +#define EMC_TPPD 0xac +#define EMC_ODT_WRITE 0xb0 +#define EMC_PDEX2MRR 0xb4 +#define EMC_WEXT 0xb8 +#define EMC_RFC_SLR 0xc0 +#define EMC_MRS_WAIT_CNT2 0xc4 +#define EMC_MRS_WAIT_CNT2_MRS_EXT2_WAIT_CNT_SHIFT 16 +#define EMC_MRS_WAIT_CNT2_MRS_EXT1_WAIT_CNT_SHIFT 0 +#define EMC_MRS_WAIT_CNT 0xc8 +#define EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT 0 +#define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK \ + (0x3FF << EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT) + +#define EMC_MRS 0xcc +#define EMC_EMRS 0xd0 +#define EMC_EMRS_USE_EMRS_LONG_CNT BIT(26) +#define EMC_REF 0xd4 +#define EMC_REF_REF_CMD BIT(0) +#define EMC_SELF_REF 0xe0 +#define EMC_MRW 0xe8 +#define EMC_MRW_MRW_OP_SHIFT 0 +#define EMC_MRW_MRW_OP_MASK \ + (0xff << EMC_MRW_MRW_OP_SHIFT) +#define EMC_MRW_MRW_MA_SHIFT 16 +#define EMC_MRW_USE_MRW_EXT_CNT 27 +#define EMC_MRW_MRW_DEV_SELECTN_SHIFT 30 + +#define EMC_MRR 0xec +#define EMC_MRR_DEV_SEL_SHIFT 30 +#define EMC_MRR_DEV_SEL_MASK 0x3 +#define EMC_MRR_MA_SHIFT 16 +#define EMC_MRR_MA_MASK 0xff +#define EMC_MRR_DATA_SHIFT 0 +#define EMC_MRR_DATA_MASK 0xffff + +#define EMC_FBIO_SPARE 0x100 +#define EMC_FBIO_CFG5 0x104 +#define EMC_FBIO_CFG5_DRAM_TYPE_SHIFT 0 +#define EMC_FBIO_CFG5_DRAM_TYPE_MASK \ + (0x3 << EMC_FBIO_CFG5_DRAM_TYPE_SHIFT) +#define EMC_FBIO_CFG5_CMD_TX_DIS BIT(8) + +#define EMC_PDEX2CKE 0x118 +#define EMC_CKE2PDEN 0x11c +#define EMC_MPC 0x128 +#define EMC_EMRS2 0x12c +#define EMC_EMRS2_USE_EMRS2_LONG_CNT BIT(26) +#define EMC_MRW2 0x134 +#define EMC_MRW3 0x138 +#define EMC_MRW4 0x13c +#define EMC_R2R 0x144 +#define EMC_EINPUT 0x14c +#define EMC_EINPUT_DURATION 0x150 +#define EMC_PUTERM_EXTRA 0x154 +#define EMC_TCKESR 0x158 +#define EMC_TPD 0x15c +#define EMC_AUTO_CAL_CONFIG 0x2a4 +#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_COMPUTE_START BIT(0) +#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL BIT(9) +#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL BIT(10) +#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE BIT(29) +#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_START BIT(31) +#define EMC_EMC_STATUS 0x2b4 +#define EMC_EMC_STATUS_MRR_DIVLD BIT(20) +#define EMC_EMC_STATUS_TIMING_UPDATE_STALLED BIT(23) +#define EMC_EMC_STATUS_DRAM_IN_POWERDOWN_SHIFT 4 +#define EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK \ + (0x3 << EMC_EMC_STATUS_DRAM_IN_POWERDOWN_SHIFT) +#define EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_SHIFT 8 +#define EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK \ + (0x3 << EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_SHIFT) + +#define EMC_CFG_2 0x2b8 +#define EMC_CFG_DIG_DLL 0x2bc +#define EMC_CFG_DIG_DLL_CFG_DLL_EN BIT(0) +#define EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK BIT(1) +#define EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC BIT(3) +#define EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK BIT(4) +#define EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT 6 +#define EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK \ + (0x3 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT) +#define EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_SHIFT 8 +#define EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_MASK \ + (0x7 << EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_SHIFT) + +#define EMC_CFG_DIG_DLL_PERIOD 0x2c0 +#define EMC_DIG_DLL_STATUS 0x2c4 +#define EMC_DIG_DLL_STATUS_DLL_LOCK BIT(15) +#define EMC_DIG_DLL_STATUS_DLL_PRIV_UPDATED BIT(17) +#define EMC_DIG_DLL_STATUS_DLL_OUT_SHIFT 0 +#define EMC_DIG_DLL_STATUS_DLL_OUT_MASK \ + (0x7ff << EMC_DIG_DLL_STATUS_DLL_OUT_SHIFT) + +#define EMC_CFG_DIG_DLL_1 0x2c8 +#define EMC_RDV_MASK 0x2cc +#define EMC_WDV_MASK 0x2d0 +#define EMC_RDV_EARLY_MASK 0x2d4 +#define EMC_RDV_EARLY 0x2d8 +#define EMC_AUTO_CAL_CONFIG8 0x2dc +#define EMC_ZCAL_INTERVAL 0x2e0 +#define EMC_ZCAL_WAIT_CNT 0x2e4 +#define EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK 0x7ff +#define EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_SHIFT 0 + +#define EMC_ZQ_CAL 0x2ec +#define EMC_ZQ_CAL_DEV_SEL_SHIFT 30 +#define EMC_ZQ_CAL_LONG BIT(4) +#define EMC_ZQ_CAL_ZQ_LATCH_CMD BIT(1) +#define EMC_ZQ_CAL_ZQ_CAL_CMD BIT(0) +#define EMC_FDPD_CTRL_DQ 0x310 +#define EMC_FDPD_CTRL_CMD 0x314 +#define EMC_PMACRO_CMD_BRICK_CTRL_FDPD 0x318 +#define EMC_PMACRO_DATA_BRICK_CTRL_FDPD 0x31c +#define EMC_PMACRO_BRICK_CTRL_RFU1 0x330 +#define EMC_PMACRO_BRICK_CTRL_RFU2 0x334 +#define EMC_TR_TIMING_0 0x3b4 +#define EMC_TR_CTRL_1 0x3bc +#define EMC_TR_RDV 0x3c4 +#define EMC_STALL_THEN_EXE_AFTER_CLKCHANGE 0x3cc +#define EMC_SEL_DPD_CTRL 0x3d8 +#define EMC_SEL_DPD_CTRL_DATA_SEL_DPD_EN BIT(8) +#define EMC_SEL_DPD_CTRL_ODT_SEL_DPD_EN BIT(5) +#define EMC_SEL_DPD_CTRL_RESET_SEL_DPD_EN BIT(4) +#define EMC_SEL_DPD_CTRL_CA_SEL_DPD_EN BIT(3) +#define EMC_SEL_DPD_CTRL_CLK_SEL_DPD_EN BIT(2) +#define EMC_PRE_REFRESH_REQ_CNT 0x3dc +#define EMC_DYN_SELF_REF_CONTROL 0x3e0 +#define EMC_TXSRDLL 0x3e4 +#define EMC_CCFIFO_ADDR 0x3e8 +#define EMC_CCFIFO_ADDR_STALL_BY_1 (1 << 31) +#define EMC_CCFIFO_ADDR_STALL(x) (((x) & 0x7fff) << 16) +#define EMC_CCFIFO_ADDR_OFFSET(x) ((x) & 0xffff) +#define EMC_CCFIFO_DATA 0x3ec +#define EMC_TR_QPOP 0x3f4 +#define EMC_TR_RDV_MASK 0x3f8 +#define EMC_TR_QSAFE 0x3fc +#define EMC_TR_QRST 0x400 +#define EMC_ISSUE_QRST 0x428 +#define EMC_AUTO_CAL_CONFIG2 0x458 +#define EMC_AUTO_CAL_CONFIG3 0x45c +#define EMC_TR_DVFS 0x460 +#define EMC_AUTO_CAL_CHANNEL 0x464 +#define EMC_IBDLY 0x468 +#define EMC_OBDLY 0x46c +#define EMC_TXDSRVTTGEN 0x480 +#define EMC_WE_DURATION 0x48c +#define EMC_WS_DURATION 0x490 +#define EMC_WEV 0x494 +#define EMC_WSV 0x498 +#define EMC_CFG_3 0x49c +#define EMC_MRW6 0x4a4 +#define EMC_MRW7 0x4a8 +#define EMC_MRW8 0x4ac +#define EMC_MRW9 0x4b0 +#define EMC_MRW10 0x4b4 +#define EMC_MRW11 0x4b8 +#define EMC_MRW12 0x4bc +#define EMC_MRW13 0x4c0 +#define EMC_MRW14 0x4c4 +#define EMC_MRW15 0x4d0 +#define EMC_CFG_SYNC 0x4d4 +#define EMC_FDPD_CTRL_CMD_NO_RAMP 0x4d8 +#define EMC_FDPD_CTRL_CMD_NO_RAMP_CMD_DPD_NO_RAMP_ENABLE BIT(0) +#define EMC_WDV_CHK 0x4e0 +#define EMC_CFG_PIPE_2 0x554 +#define EMC_CFG_PIPE_CLK 0x558 +#define EMC_CFG_PIPE_CLK_CLK_ALWAYS_ON BIT(0) +#define EMC_CFG_PIPE_1 0x55c +#define EMC_CFG_PIPE 0x560 +#define EMC_QPOP 0x564 +#define EMC_QUSE_WIDTH 0x568 +#define EMC_PUTERM_WIDTH 0x56c +#define EMC_AUTO_CAL_CONFIG7 0x574 +#define EMC_REFCTRL2 0x580 +#define EMC_FBIO_CFG7 0x584 +#define EMC_FBIO_CFG7_CH0_ENABLE BIT(1) +#define EMC_FBIO_CFG7_CH1_ENABLE BIT(2) +#define EMC_DATA_BRLSHFT_0 0x588 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_SHIFT 21 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_SHIFT 18 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_SHIFT 15 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_SHIFT 12 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_SHIFT 9 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_SHIFT 6 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_SHIFT 3 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_SHIFT 0 +#define EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_SHIFT) + +#define EMC_DATA_BRLSHFT_1 0x58c +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_SHIFT 21 +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_SHIFT 18 +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_SHIFT 15 +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_SHIFT 12 +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_SHIFT 9 +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_SHIFT 6 +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_SHIFT 3 +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_SHIFT) +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_SHIFT 0 +#define EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_MASK \ + (0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_SHIFT) + +#define EMC_RFCPB 0x590 +#define EMC_DQS_BRLSHFT_0 0x594 +#define EMC_DQS_BRLSHFT_1 0x598 +#define EMC_CMD_BRLSHFT_0 0x59c +#define EMC_CMD_BRLSHFT_1 0x5a0 +#define EMC_CMD_BRLSHFT_2 0x5a4 +#define EMC_CMD_BRLSHFT_3 0x5a8 +#define EMC_QUSE_BRLSHFT_0 0x5ac +#define EMC_AUTO_CAL_CONFIG4 0x5b0 +#define EMC_AUTO_CAL_CONFIG5 0x5b4 +#define EMC_QUSE_BRLSHFT_1 0x5b8 +#define EMC_QUSE_BRLSHFT_2 0x5bc +#define EMC_CCDMW 0x5c0 +#define EMC_QUSE_BRLSHFT_3 0x5c4 +#define EMC_AUTO_CAL_CONFIG6 0x5cc +#define EMC_DLL_CFG_0 0x5e4 +#define EMC_DLL_CFG_1 0x5e8 +#define EMC_DLL_CFG_1_DDLLCAL_CTRL_START_TRIM_SHIFT 10 +#define EMC_DLL_CFG_1_DDLLCAL_CTRL_START_TRIM_MASK \ + (0x7ff << EMC_DLL_CFG_1_DDLLCAL_CTRL_START_TRIM_SHIFT) + +#define EMC_CONFIG_SAMPLE_DELAY 0x5f0 +#define EMC_CFG_UPDATE 0x5f4 +#define EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_SHIFT 9 +#define EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_MASK \ + (0x3 << EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_SHIFT) + +#define EMC_PMACRO_QUSE_DDLL_RANK0_0 0x600 +#define EMC_PMACRO_QUSE_DDLL_RANK0_1 0x604 +#define EMC_PMACRO_QUSE_DDLL_RANK0_2 0x608 +#define EMC_PMACRO_QUSE_DDLL_RANK0_3 0x60c +#define EMC_PMACRO_QUSE_DDLL_RANK0_4 0x610 +#define EMC_PMACRO_QUSE_DDLL_RANK0_5 0x614 +#define EMC_PMACRO_QUSE_DDLL_RANK1_0 0x620 +#define EMC_PMACRO_QUSE_DDLL_RANK1_1 0x624 +#define EMC_PMACRO_QUSE_DDLL_RANK1_2 0x628 +#define EMC_PMACRO_QUSE_DDLL_RANK1_3 0x62c +#define EMC_PMACRO_QUSE_DDLL_RANK1_4 0x630 +#define EMC_PMACRO_QUSE_DDLL_RANK1_5 0x634 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 0x640 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE1_SHIFT \ + 16 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE1_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE1_SHIFT) +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE0_SHIFT \ + 0 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE0_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE0_SHIFT) + +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 0x644 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE3_SHIFT \ + 16 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE3_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE3_SHIFT) +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE2_SHIFT \ + 0 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE2_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE2_SHIFT) + +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 0x648 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE5_SHIFT \ + 16 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE5_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE5_SHIFT) +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE4_SHIFT \ + 0 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE4_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE4_SHIFT) + +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 0x64c +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE7_SHIFT \ + 16 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE7_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE7_SHIFT) +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE6_SHIFT \ + 0 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE6_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE6_SHIFT) + +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4 0x650 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5 0x654 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 0x660 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE1_SHIFT \ + 16 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE1_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE1_SHIFT) +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE0_SHIFT \ + 0 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE0_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE0_SHIFT) + +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 0x664 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE3_SHIFT \ + 16 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE3_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE3_SHIFT) +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE2_SHIFT \ + 0 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE2_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE2_SHIFT) + +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 0x668 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE5_SHIFT \ + 16 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE5_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE5_SHIFT) +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE4_SHIFT \ + 0 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE4_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE4_SHIFT) + +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 0x66c +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE7_SHIFT \ + 16 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE7_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE7_SHIFT) +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE6_SHIFT \ + 0 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE6_MASK \ + (0x3ff << \ + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE6_SHIFT) + +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4 0x670 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5 0x674 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0 0x680 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1 0x684 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2 0x688 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3 0x68c +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4 0x690 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5 0x694 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0 0x6a0 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1 0x6a4 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2 0x6a8 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3 0x6ac +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4 0x6b0 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5 0x6b4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0 0x6c0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1 0x6c4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2 0x6c8 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3 0x6cc +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0 0x6e0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1 0x6e4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2 0x6e8 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3 0x6ec +#define EMC_PMACRO_TX_PWRD_0 0x720 +#define EMC_PMACRO_TX_PWRD_1 0x724 +#define EMC_PMACRO_TX_PWRD_2 0x728 +#define EMC_PMACRO_TX_PWRD_3 0x72c +#define EMC_PMACRO_TX_PWRD_4 0x730 +#define EMC_PMACRO_TX_PWRD_5 0x734 +#define EMC_PMACRO_TX_SEL_CLK_SRC_0 0x740 +#define EMC_PMACRO_TX_SEL_CLK_SRC_1 0x744 +#define EMC_PMACRO_TX_SEL_CLK_SRC_3 0x74c +#define EMC_PMACRO_TX_SEL_CLK_SRC_2 0x748 +#define EMC_PMACRO_TX_SEL_CLK_SRC_4 0x750 +#define EMC_PMACRO_TX_SEL_CLK_SRC_5 0x754 +#define EMC_PMACRO_DDLL_BYPASS 0x760 +#define EMC_PMACRO_DDLL_PWRD_0 0x770 +#define EMC_PMACRO_DDLL_PWRD_1 0x774 +#define EMC_PMACRO_DDLL_PWRD_2 0x778 +#define EMC_PMACRO_CMD_CTRL_0 0x780 +#define EMC_PMACRO_CMD_CTRL_1 0x784 +#define EMC_PMACRO_CMD_CTRL_2 0x788 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_0 0x800 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_1 0x804 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_2 0x808 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_3 0x80c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_0 0x810 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_1 0x814 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_2 0x818 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_3 0x81c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_0 0x820 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_1 0x824 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_2 0x828 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_3 0x82c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_0 0x830 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_1 0x834 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_2 0x838 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_3 0x83c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_0 0x840 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_1 0x844 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_2 0x848 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_3 0x84c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_0 0x850 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_1 0x854 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_2 0x858 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_3 0x85c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_0 0x860 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_1 0x864 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_2 0x868 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_3 0x86c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_0 0x870 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_1 0x874 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_2 0x878 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_3 0x87c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_0 0x880 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_1 0x884 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_2 0x888 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_3 0x88c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_0 0x890 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_1 0x894 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_2 0x898 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_3 0x89c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_0 0x8a0 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_1 0x8a4 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_2 0x8a8 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_3 0x8ac +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_0 0x8b0 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_1 0x8b4 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_2 0x8b8 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_3 0x8bc +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_0 0x900 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_1 0x904 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_2 0x908 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_3 0x90c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_0 0x910 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_1 0x914 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_2 0x918 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_3 0x91c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_0 0x920 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_1 0x924 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_2 0x928 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_3 0x92c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_0 0x930 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_1 0x934 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_2 0x938 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_3 0x93c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_0 0x940 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_1 0x944 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_2 0x948 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_3 0x94c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_0 0x950 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_1 0x954 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_2 0x958 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_3 0x95c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_0 0x960 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_1 0x964 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_2 0x968 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_3 0x96c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_0 0x970 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_1 0x974 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_2 0x978 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_3 0x97c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_0 0x980 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_1 0x984 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_2 0x988 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_3 0x98c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_0 0x990 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_1 0x994 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_2 0x998 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_3 0x99c +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_0 0x9a0 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_1 0x9a4 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_2 0x9a8 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_3 0x9ac +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_0 0x9b0 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_1 0x9b4 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_2 0x9b8 +#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_3 0x9bc +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_0 0xa00 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_1 0xa04 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_2 0xa08 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_0 0xa10 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_1 0xa14 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_2 0xa18 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_0 0xa20 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_1 0xa24 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_2 0xa28 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_0 0xa30 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_1 0xa34 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_2 0xa38 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_0 0xa40 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_1 0xa44 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_2 0xa48 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_0 0xa50 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_1 0xa54 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_2 0xa58 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_0 0xa60 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_1 0xa64 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_2 0xa68 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_0 0xa70 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_1 0xa74 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_2 0xa78 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_0 0xb00 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_1 0xb04 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_2 0xb08 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_0 0xb10 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_1 0xb14 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_2 0xb18 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_0 0xb20 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_1 0xb24 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_2 0xb28 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_0 0xb30 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_1 0xb34 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_2 0xb38 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_0 0xb40 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_1 0xb44 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_2 0xb48 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_0 0xb50 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_1 0xb54 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_2 0xb58 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_0 0xb60 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_1 0xb64 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_2 0xb68 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_0 0xb70 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_1 0xb74 +#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_2 0xb78 +#define EMC_PMACRO_IB_VREF_DQ_0 0xbe0 +#define EMC_PMACRO_IB_VREF_DQ_1 0xbe4 +#define EMC_PMACRO_IB_VREF_DQS_0 0xbf0 +#define EMC_PMACRO_IB_VREF_DQS_1 0xbf4 +#define EMC_PMACRO_DDLL_LONG_CMD_0 0xc00 +#define EMC_PMACRO_DDLL_LONG_CMD_1 0xc04 +#define EMC_PMACRO_DDLL_LONG_CMD_2 0xc08 +#define EMC_PMACRO_DDLL_LONG_CMD_3 0xc0c +#define EMC_PMACRO_DDLL_LONG_CMD_4 0xc10 +#define EMC_PMACRO_DDLL_LONG_CMD_5 0xc14 +#define EMC_PMACRO_DDLL_SHORT_CMD_0 0xc20 +#define EMC_PMACRO_DDLL_SHORT_CMD_1 0xc24 +#define EMC_PMACRO_DDLL_SHORT_CMD_2 0xc28 +#define EMC_PMACRO_CFG_PM_GLOBAL_0 0xc30 +#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0 BIT(16) +#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1 BIT(17) +#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2 BIT(18) +#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3 BIT(19) +#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4 BIT(20) +#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5 BIT(21) +#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6 BIT(22) +#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7 BIT(23) +#define EMC_PMACRO_VTTGEN_CTRL_0 0xc34 +#define EMC_PMACRO_VTTGEN_CTRL_1 0xc38 +#define EMC_PMACRO_BG_BIAS_CTRL_0 0xc3c +#define EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD BIT(0) +#define EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD BIT(2) +#define EMC_PMACRO_PAD_CFG_CTRL 0xc40 +#define EMC_PMACRO_ZCTRL 0xc44 +#define EMC_PMACRO_CMD_PAD_RX_CTRL 0xc50 +#define EMC_PMACRO_DATA_PAD_RX_CTRL 0xc54 +#define EMC_PMACRO_CMD_RX_TERM_MODE 0xc58 +#define EMC_PMACRO_DATA_RX_TERM_MODE 0xc5c +#define EMC_PMACRO_CMD_PAD_TX_CTRL 0xc60 +#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC BIT(1) +#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC BIT(9) +#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC BIT(16) +#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC BIT(24) +#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON BIT(26) + +#define EMC_PMACRO_DATA_PAD_TX_CTRL 0xc64 +#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF BIT(0) +#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC BIT(1) +#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF BIT(8) +#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC BIT(9) +#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC BIT(16) +#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC BIT(24) + +#define EMC_PMACRO_COMMON_PAD_TX_CTRL 0xc68 +#define EMC_PMACRO_AUTOCAL_CFG_COMMON 0xc78 +#define EMC_PMACRO_AUTOCAL_CFG_COMMON_E_CAL_BYPASS_DVFS BIT(16) +#define EMC_PMACRO_VTTGEN_CTRL_2 0xcf0 +#define EMC_PMACRO_IB_RXRT 0xcf4 +#define EMC_PMACRO_TRAINING_CTRL_0 0xcf8 +#define EMC_PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR BIT(3) +#define EMC_PMACRO_TRAINING_CTRL_1 0xcfc +#define EMC_PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR BIT(3) +#define EMC_TRAINING_CTRL 0xe04 +#define EMC_TRAINING_QUSE_CORS_CTRL 0xe0c +#define EMC_TRAINING_QUSE_FINE_CTRL 0xe10 +#define EMC_TRAINING_QUSE_CTRL_MISC 0xe14 +#define EMC_TRAINING_WRITE_FINE_CTRL 0xe18 +#define EMC_TRAINING_WRITE_CTRL_MISC 0xe1c +#define EMC_TRAINING_WRITE_VREF_CTRL 0xe20 +#define EMC_TRAINING_READ_FINE_CTRL 0xe24 +#define EMC_TRAINING_READ_CTRL_MISC 0xe28 +#define EMC_TRAINING_READ_VREF_CTRL 0xe2c +#define EMC_TRAINING_CA_FINE_CTRL 0xe30 +#define EMC_TRAINING_CA_CTRL_MISC 0xe34 +#define EMC_TRAINING_CA_CTRL_MISC1 0xe38 +#define EMC_TRAINING_CA_VREF_CTRL 0xe3c +#define EMC_TRAINING_SETTLE 0xe44 +#define EMC_TRAINING_MPC 0xe5c +#define EMC_TRAINING_VREF_SETTLE 0xe6c +#define EMC_TRAINING_QUSE_VREF_CTRL 0xed0 +#define EMC_TRAINING_OPT_DQS_IB_VREF_RANK0 0xed4 +#define EMC_TRAINING_OPT_DQS_IB_VREF_RANK1 0xed8 + +#define EMC_COPY_TABLE_PARAM_PERIODIC_FIELDS BIT(0) +#define EMC_COPY_TABLE_PARAM_TRIM_REGS BIT(1) + +enum burst_regs_list { + EMC_RP_INDEX = 6, + EMC_R2P_INDEX = 9, + EMC_W2P_INDEX, + EMC_MRW6_INDEX = 31, + EMC_REFRESH_INDEX = 41, + EMC_PRE_REFRESH_REQ_CNT_INDEX = 43, + EMC_TRPAB_INDEX = 59, + EMC_MRW7_INDEX = 62, + EMC_FBIO_CFG5_INDEX = 65, + EMC_FBIO_CFG7_INDEX, + EMC_CFG_DIG_DLL_INDEX, + EMC_ZCAL_INTERVAL_INDEX = 139, + EMC_ZCAL_WAIT_CNT_INDEX, + EMC_MRS_WAIT_CNT_INDEX = 141, + EMC_DLL_CFG_0_INDEX = 144, + EMC_PMACRO_AUTOCAL_CFG_COMMON_INDEX = 146, + EMC_CFG_INDEX = 148, + EMC_DYN_SELF_REF_CONTROL_INDEX = 150, + EMC_PMACRO_CMD_PAD_TX_CTRL_INDEX = 161, + EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX, + EMC_PMACRO_COMMON_PAD_TX_CTRL_INDEX, + EMC_PMACRO_BRICK_CTRL_RFU1_INDEX = 167, + EMC_PMACRO_BG_BIAS_CTRL_0_INDEX = 171, + EMC_MRW14_INDEX = 199, + EMC_MRW15_INDEX = 220, +}; + +enum trim_regs_list { + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_INDEX = 60, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_INDEX, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_INDEX, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_INDEX, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4_INDEX, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5_INDEX, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_INDEX, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_INDEX, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_INDEX, + EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_INDEX, +}; + +enum burst_mc_regs_list { + MC_EMEM_ARB_MISC0_INDEX = 20, +}; + +enum { + T_RP, + T_FC_LPDDR4, + T_RFC, + T_PDEX, + RL, +}; + +enum { + AUTO_PD = 0, + MAN_SR = 2, +}; + +enum { + ASSEMBLY = 0, + ACTIVE, +}; + +enum { + C0D0U0, + C0D0U1, + C0D1U0, + C0D1U1, + C1D0U0, + C1D0U1, + C1D1U0, + C1D1U1, + DRAM_CLKTREE_NUM, +}; + +#define VREF_REGS_PER_CHANNEL_SIZE 4 +#define DRAM_TIMINGS_NUM 5 +#define BURST_REGS_PER_CHANNEL_SIZE 8 +#define TRIM_REGS_PER_CHANNEL_SIZE 10 +#define PTFV_ARRAY_SIZE 12 +#define SAVE_RESTORE_MOD_REGS_SIZE 12 +#define TRAINING_MOD_REGS_SIZE 20 +#define BURST_UP_DOWN_REGS_SIZE 24 +#define BURST_MC_REGS_SIZE 33 +#define TRIM_REGS_SIZE 138 +#define BURST_REGS_SIZE 221 + +struct tegra210_emc_per_channel_regs { + u16 bank; + u16 offset; +}; + +struct tegra210_emc_table_register_offsets { + u16 burst[BURST_REGS_SIZE]; + u16 trim[TRIM_REGS_SIZE]; + u16 burst_mc[BURST_MC_REGS_SIZE]; + u16 la_scale[BURST_UP_DOWN_REGS_SIZE]; + struct tegra210_emc_per_channel_regs burst_per_channel[BURST_REGS_PER_CHANNEL_SIZE]; + struct tegra210_emc_per_channel_regs trim_per_channel[TRIM_REGS_PER_CHANNEL_SIZE]; + struct tegra210_emc_per_channel_regs vref_per_channel[VREF_REGS_PER_CHANNEL_SIZE]; +}; + +struct tegra210_emc_timing { + u32 revision; + const char dvfs_ver[60]; + u32 rate; + u32 min_volt; + u32 gpu_min_volt; + const char clock_src[32]; + u32 clk_src_emc; + u32 needs_training; + u32 training_pattern; + u32 trained; + + u32 periodic_training; + u32 trained_dram_clktree[DRAM_CLKTREE_NUM]; + u32 current_dram_clktree[DRAM_CLKTREE_NUM]; + u32 run_clocks; + u32 tree_margin; + + u32 num_burst; + u32 num_burst_per_ch; + u32 num_trim; + u32 num_trim_per_ch; + u32 num_mc_regs; + u32 num_up_down; + u32 vref_num; + u32 training_mod_num; + u32 dram_timing_num; + + u32 ptfv_list[PTFV_ARRAY_SIZE]; + + u32 burst_regs[BURST_REGS_SIZE]; + u32 burst_reg_per_ch[BURST_REGS_PER_CHANNEL_SIZE]; + u32 shadow_regs_ca_train[BURST_REGS_SIZE]; + u32 shadow_regs_quse_train[BURST_REGS_SIZE]; + u32 shadow_regs_rdwr_train[BURST_REGS_SIZE]; + + u32 trim_regs[TRIM_REGS_SIZE]; + u32 trim_perch_regs[TRIM_REGS_PER_CHANNEL_SIZE]; + + u32 vref_perch_regs[VREF_REGS_PER_CHANNEL_SIZE]; + + u32 dram_timings[DRAM_TIMINGS_NUM]; + u32 training_mod_regs[TRAINING_MOD_REGS_SIZE]; + u32 save_restore_mod_regs[SAVE_RESTORE_MOD_REGS_SIZE]; + u32 burst_mc_regs[BURST_MC_REGS_SIZE]; + u32 la_scale_regs[BURST_UP_DOWN_REGS_SIZE]; + + u32 min_mrs_wait; + u32 emc_mrw; + u32 emc_mrw2; + u32 emc_mrw3; + u32 emc_mrw4; + u32 emc_mrw9; + u32 emc_mrs; + u32 emc_emrs; + u32 emc_emrs2; + u32 emc_auto_cal_config; + u32 emc_auto_cal_config2; + u32 emc_auto_cal_config3; + u32 emc_auto_cal_config4; + u32 emc_auto_cal_config5; + u32 emc_auto_cal_config6; + u32 emc_auto_cal_config7; + u32 emc_auto_cal_config8; + u32 emc_cfg_2; + u32 emc_sel_dpd_ctrl; + u32 emc_fdpd_ctrl_cmd_no_ramp; + u32 dll_clk_src; + u32 clk_out_enb_x_0_clk_enb_emc_dll; + u32 latency; +}; + +enum tegra210_emc_refresh { + TEGRA210_EMC_REFRESH_NOMINAL = 0, + TEGRA210_EMC_REFRESH_2X, + TEGRA210_EMC_REFRESH_4X, + TEGRA210_EMC_REFRESH_THROTTLE, /* 4x Refresh + derating. */ +}; + +#define DRAM_TYPE_DDR3 0 +#define DRAM_TYPE_LPDDR4 1 +#define DRAM_TYPE_LPDDR2 2 +#define DRAM_TYPE_DDR2 3 + +struct tegra210_emc { + struct tegra_mc *mc; + struct device *dev; + struct clk *clk; + + /* nominal EMC frequency table */ + struct tegra210_emc_timing *nominal; + /* derated EMC frequency table */ + struct tegra210_emc_timing *derated; + + /* currently selected table (nominal or derated) */ + struct tegra210_emc_timing *timings; + unsigned int num_timings; + + const struct tegra210_emc_table_register_offsets *offsets; + + const struct tegra210_emc_sequence *sequence; + spinlock_t lock; + + void __iomem *regs, *channel[2]; + unsigned int num_channels; + unsigned int num_devices; + unsigned int dram_type; + + struct tegra210_emc_timing *last; + struct tegra210_emc_timing *next; + + unsigned int training_interval; + struct timer_list training; + + enum tegra210_emc_refresh refresh; + unsigned int refresh_poll_interval; + struct timer_list refresh_timer; + unsigned int temperature; + atomic_t refresh_poll; + + ktime_t clkchange_time; + int clkchange_delay; + + unsigned long resume_rate; + + struct { + struct dentry *root; + unsigned long min_rate; + unsigned long max_rate; + unsigned int temperature; + } debugfs; + + struct tegra210_clk_emc_provider provider; +}; + +struct tegra210_emc_sequence { + u8 revision; + void (*set_clock)(struct tegra210_emc *emc, u32 clksrc); + u32 (*periodic_compensation)(struct tegra210_emc *emc); +}; + +static inline void emc_writel(struct tegra210_emc *emc, u32 value, + unsigned int offset) +{ + writel_relaxed(value, emc->regs + offset); +} + +static inline u32 emc_readl(struct tegra210_emc *emc, unsigned int offset) +{ + return readl_relaxed(emc->regs + offset); +} + +static inline void emc_channel_writel(struct tegra210_emc *emc, + unsigned int channel, + u32 value, unsigned int offset) +{ + writel_relaxed(value, emc->channel[channel] + offset); +} + +static inline u32 emc_channel_readl(struct tegra210_emc *emc, + unsigned int channel, unsigned int offset) +{ + return readl_relaxed(emc->channel[channel] + offset); +} + +static inline void ccfifo_writel(struct tegra210_emc *emc, u32 value, + unsigned int offset, u32 delay) +{ + writel_relaxed(value, emc->regs + EMC_CCFIFO_DATA); + + value = EMC_CCFIFO_ADDR_STALL_BY_1 | EMC_CCFIFO_ADDR_STALL(delay) | + EMC_CCFIFO_ADDR_OFFSET(offset); + writel_relaxed(value, emc->regs + EMC_CCFIFO_ADDR); +} + +static inline u32 div_o3(u32 a, u32 b) +{ + u32 result = a / b; + + if ((b * result) < a) + return result + 1; + + return result; +} + +/* from tegra210-emc-r21021.c */ +extern const struct tegra210_emc_sequence tegra210_emc_r21021; + +int tegra210_emc_set_refresh(struct tegra210_emc *emc, + enum tegra210_emc_refresh refresh); +u32 tegra210_emc_mrr_read(struct tegra210_emc *emc, unsigned int chip, + unsigned int address); +void tegra210_emc_do_clock_change(struct tegra210_emc *emc, u32 clksrc); +void tegra210_emc_set_shadow_bypass(struct tegra210_emc *emc, int set); +void tegra210_emc_timing_update(struct tegra210_emc *emc); +u32 tegra210_emc_get_dll_state(struct tegra210_emc_timing *next); +struct tegra210_emc_timing *tegra210_emc_find_timing(struct tegra210_emc *emc, + unsigned long rate); +void tegra210_emc_adjust_timing(struct tegra210_emc *emc, + struct tegra210_emc_timing *timing); +int tegra210_emc_wait_for_update(struct tegra210_emc *emc, unsigned int channel, + unsigned int offset, u32 bit_mask, bool state); +unsigned long tegra210_emc_actual_osc_clocks(u32 in); +u32 tegra210_emc_compensate(struct tegra210_emc_timing *next, u32 offset); +void tegra210_emc_dll_disable(struct tegra210_emc *emc); +void tegra210_emc_dll_enable(struct tegra210_emc *emc); +u32 tegra210_emc_dll_prelock(struct tegra210_emc *emc, u32 clksrc); +u32 tegra210_emc_dvfs_power_ramp_down(struct tegra210_emc *emc, u32 clk, + bool flip_backward); +u32 tegra210_emc_dvfs_power_ramp_up(struct tegra210_emc *emc, u32 clk, + bool flip_backward); +void tegra210_emc_reset_dram_clktree_values(struct tegra210_emc_timing *timing); +void tegra210_emc_start_periodic_compensation(struct tegra210_emc *emc); + +#endif diff --git a/drivers/memory/tegra/tegra210-mc.h b/drivers/memory/tegra/tegra210-mc.h new file mode 100644 index 000000000000..b9b91ceb4730 --- /dev/null +++ b/drivers/memory/tegra/tegra210-mc.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved. + */ + +#ifndef TEGRA210_MC_H +#define TEGRA210_MC_H + +#include "mc.h" + +/* register definitions */ +#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2e4 +#define MC_LATENCY_ALLOWANCE_HC_0 0x310 +#define MC_LATENCY_ALLOWANCE_HC_1 0x314 +#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320 +#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328 +#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344 +#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348 +#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370 +#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374 +#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37c +#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380 +#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390 +#define MC_LATENCY_ALLOWANCE_VIC_0 0x394 +#define MC_LATENCY_ALLOWANCE_VI2_0 0x398 +#define MC_LATENCY_ALLOWANCE_GPU_0 0x3ac +#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3b8 +#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3bc +#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3c0 +#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3c4 +#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3e8 +#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3d8 +#define MC_MLL_MPCORER_PTSA_RATE 0x44c +#define MC_FTOP_PTSA_RATE 0x50c +#define MC_EMEM_ARB_TIMING_RFCPB 0x6c0 +#define MC_EMEM_ARB_TIMING_CCDMW 0x6c4 +#define MC_EMEM_ARB_REFPB_HP_CTRL 0x6f0 +#define MC_EMEM_ARB_REFPB_BANK_CTRL 0x6f4 +#define MC_PTSA_GRANT_DECREMENT 0x960 +#define MC_EMEM_ARB_DHYST_CTRL 0xbcc +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xbd0 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xbd4 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xbd8 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xbdc +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xbe0 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xbe4 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xbe8 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xbec + +#endif diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c index b42bdb667e85..055af0e08a2e 100644 --- a/drivers/memory/tegra/tegra30-emc.c +++ b/drivers/memory/tegra/tegra30-emc.c @@ -11,7 +11,6 @@ #include <linux/clk.h> #include <linux/clk/tegra.h> -#include <linux/completion.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/err.h> @@ -327,7 +326,6 @@ struct emc_timing { struct tegra_emc { struct device *dev; struct tegra_mc *mc; - struct completion clk_handshake_complete; struct notifier_block clk_nb; struct clk *clk; void __iomem *regs; @@ -374,52 +372,10 @@ static int emc_seq_update_timing(struct tegra_emc *emc) return 0; } -static void emc_complete_clk_change(struct tegra_emc *emc) -{ - struct emc_timing *timing = emc->new_timing; - unsigned int dram_num; - bool failed = false; - int err; - - /* re-enable auto-refresh */ - dram_num = tegra_mc_get_emem_device_count(emc->mc); - writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num), - emc->regs + EMC_REFCTRL); - - /* restore auto-calibration */ - if (emc->vref_cal_toggle) - writel_relaxed(timing->emc_auto_cal_interval, - emc->regs + EMC_AUTO_CAL_INTERVAL); - - /* restore dynamic self-refresh */ - if (timing->emc_cfg_dyn_self_ref) { - emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE; - writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG); - } - - /* set number of clocks to wait after each ZQ command */ - if (emc->zcal_long) - writel_relaxed(timing->emc_zcal_cnt_long, - emc->regs + EMC_ZCAL_WAIT_CNT); - - /* wait for writes to settle */ - udelay(2); - - /* update restored timing */ - err = emc_seq_update_timing(emc); - if (err) - failed = true; - - /* restore early ACK */ - mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE); - - WRITE_ONCE(emc->bad_state, failed); -} - static irqreturn_t tegra_emc_isr(int irq, void *data) { struct tegra_emc *emc = data; - u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; + u32 intmask = EMC_REFRESH_OVERFLOW_INT; u32 status; status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask; @@ -434,18 +390,6 @@ static irqreturn_t tegra_emc_isr(int irq, void *data) /* clear interrupts */ writel_relaxed(status, emc->regs + EMC_INTSTATUS); - /* notify about EMC-CAR handshake completion */ - if (status & EMC_CLKCHANGE_COMPLETE_INT) { - if (completion_done(&emc->clk_handshake_complete)) { - dev_err_ratelimited(emc->dev, - "bogus handshake interrupt\n"); - return IRQ_NONE; - } - - emc_complete_clk_change(emc); - complete(&emc->clk_handshake_complete); - } - return IRQ_HANDLED; } @@ -801,29 +745,58 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate) */ mc_readl(emc->mc, MC_EMEM_ARB_OVERRIDE); - reinit_completion(&emc->clk_handshake_complete); - - emc->new_timing = timing; - return 0; } static int emc_complete_timing_change(struct tegra_emc *emc, unsigned long rate) { - unsigned long timeout; + struct emc_timing *timing = emc_find_timing(emc, rate); + unsigned int dram_num; + int err; + u32 v; - timeout = wait_for_completion_timeout(&emc->clk_handshake_complete, - msecs_to_jiffies(100)); - if (timeout == 0) { - dev_err(emc->dev, "emc-car handshake failed\n"); - return -EIO; + err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, v, + v & EMC_CLKCHANGE_COMPLETE_INT, + 1, 100); + if (err) { + dev_err(emc->dev, "emc-car handshake timeout: %d\n", err); + return err; } - if (READ_ONCE(emc->bad_state)) - return -EIO; + /* re-enable auto-refresh */ + dram_num = tegra_mc_get_emem_device_count(emc->mc); + writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num), + emc->regs + EMC_REFCTRL); + + /* restore auto-calibration */ + if (emc->vref_cal_toggle) + writel_relaxed(timing->emc_auto_cal_interval, + emc->regs + EMC_AUTO_CAL_INTERVAL); - return 0; + /* restore dynamic self-refresh */ + if (timing->emc_cfg_dyn_self_ref) { + emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE; + writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG); + } + + /* set number of clocks to wait after each ZQ command */ + if (emc->zcal_long) + writel_relaxed(timing->emc_zcal_cnt_long, + emc->regs + EMC_ZCAL_WAIT_CNT); + + /* wait for writes to settle */ + udelay(2); + + /* update restored timing */ + err = emc_seq_update_timing(emc); + if (!err) + emc->bad_state = false; + + /* restore early ACK */ + mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE); + + return err; } static int emc_unprepare_timing_change(struct tegra_emc *emc, @@ -1033,7 +1006,7 @@ static struct device_node *emc_find_node_by_ram_code(struct device *dev) static int emc_setup_hw(struct tegra_emc *emc) { - u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT; + u32 intmask = EMC_REFRESH_OVERFLOW_INT; u32 fbio_cfg5, emc_cfg, emc_dbg; enum emc_dram_type dram_type; @@ -1275,11 +1248,11 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc) return; } - debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root, + debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc, &tegra_emc_debug_available_rates_fops); - debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, + debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc, &tegra_emc_debug_min_rate_fops); - debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root, + debugfs_create_file("max_rate", 0644, emc->debugfs.root, emc, &tegra_emc_debug_max_rate_fops); } @@ -1321,7 +1294,6 @@ static int tegra_emc_probe(struct platform_device *pdev) if (!emc->mc) return -EPROBE_DEFER; - init_completion(&emc->clk_handshake_complete); emc->clk_nb.notifier_call = emc_clk_change_notify; emc->dev = &pdev->dev; diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c index db526dbf71ee..159a16f5e7d6 100644 --- a/drivers/memory/ti-aemif.c +++ b/drivers/memory/ti-aemif.c @@ -27,7 +27,7 @@ #define WSTROBE_SHIFT 20 #define WSETUP_SHIFT 26 #define EW_SHIFT 30 -#define SS_SHIFT 31 +#define SSTROBE_SHIFT 31 #define TA(x) ((x) << TA_SHIFT) #define RHOLD(x) ((x) << RHOLD_SHIFT) @@ -37,7 +37,7 @@ #define WSTROBE(x) ((x) << WSTROBE_SHIFT) #define WSETUP(x) ((x) << WSETUP_SHIFT) #define EW(x) ((x) << EW_SHIFT) -#define SS(x) ((x) << SS_SHIFT) +#define SSTROBE(x) ((x) << SSTROBE_SHIFT) #define ASIZE_MAX 0x1 #define TA_MAX 0x3 @@ -48,7 +48,7 @@ #define WSTROBE_MAX 0x3f #define WSETUP_MAX 0xf #define EW_MAX 0x1 -#define SS_MAX 0x1 +#define SSTROBE_MAX 0x1 #define NUM_CS 4 #define TA_VAL(x) (((x) & TA(TA_MAX)) >> TA_SHIFT) @@ -59,7 +59,7 @@ #define WSTROBE_VAL(x) (((x) & WSTROBE(WSTROBE_MAX)) >> WSTROBE_SHIFT) #define WSETUP_VAL(x) (((x) & WSETUP(WSETUP_MAX)) >> WSETUP_SHIFT) #define EW_VAL(x) (((x) & EW(EW_MAX)) >> EW_SHIFT) -#define SS_VAL(x) (((x) & SS(SS_MAX)) >> SS_SHIFT) +#define SSTROBE_VAL(x) (((x) & SSTROBE(SSTROBE_MAX)) >> SSTROBE_SHIFT) #define NRCSR_OFFSET 0x00 #define AWCCR_OFFSET 0x04 @@ -67,7 +67,7 @@ #define ACR_ASIZE_MASK 0x3 #define ACR_EW_MASK BIT(30) -#define ACR_SS_MASK BIT(31) +#define ACR_SSTROBE_MASK BIT(31) #define ASIZE_16BIT 1 #define CONFIG_MASK (TA(TA_MAX) | \ @@ -77,7 +77,7 @@ WHOLD(WHOLD_MAX) | \ WSTROBE(WSTROBE_MAX) | \ WSETUP(WSETUP_MAX) | \ - EW(EW_MAX) | SS(SS_MAX) | \ + EW(EW_MAX) | SSTROBE(SSTROBE_MAX) | \ ASIZE_MAX) /** @@ -204,7 +204,7 @@ static int aemif_config_abus(struct platform_device *pdev, int csnum) if (data->enable_ew) set |= ACR_EW_MASK; if (data->enable_ss) - set |= ACR_SS_MASK; + set |= ACR_SSTROBE_MASK; val = readl(aemif->base + offset); val &= ~CONFIG_MASK; @@ -246,7 +246,7 @@ static void aemif_get_hw_params(struct platform_device *pdev, int csnum) data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val), clk_rate); data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val), clk_rate); data->enable_ew = EW_VAL(val); - data->enable_ss = SS_VAL(val); + data->enable_ss = SSTROBE_VAL(val); data->asize = val & ASIZE_MAX; } diff --git a/drivers/memory/ti-emif-pm.c b/drivers/memory/ti-emif-pm.c index 9c90f815ad3a..6c747c1e98cb 100644 --- a/drivers/memory/ti-emif-pm.c +++ b/drivers/memory/ti-emif-pm.c @@ -248,7 +248,7 @@ MODULE_DEVICE_TABLE(of, ti_emif_of_match); static int ti_emif_resume(struct device *dev) { unsigned long tmp = - __raw_readl((void *)emif_instance->ti_emif_sram_virt); + __raw_readl((void __iomem *)emif_instance->ti_emif_sram_virt); /* * Check to see if what we are copying is already present in the diff --git a/drivers/mfd/mt6360-core.c b/drivers/mfd/mt6360-core.c index db8cdf5272c1..e9cacc27d980 100644 --- a/drivers/mfd/mt6360-core.c +++ b/drivers/mfd/mt6360-core.c @@ -412,6 +412,7 @@ MODULE_DEVICE_TABLE(of, mt6360_pmu_of_id); static struct i2c_driver mt6360_pmu_driver = { .driver = { + .name = "mt6360_pmu", .pm = &mt6360_pmu_pm_ops, .of_match_table = of_match_ptr(mt6360_pmu_of_id), }, diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index bccd341e9ae1..d5d2af4d10e6 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -828,7 +828,7 @@ static void run_plant_and_detach_test(int is_early) char before[BREAK_INSTR_SIZE]; char after[BREAK_INSTR_SIZE]; - probe_kernel_read(before, (char *)kgdbts_break_test, + copy_from_kernel_nofault(before, (char *)kgdbts_break_test, BREAK_INSTR_SIZE); init_simple_test(); ts.tst = plant_and_detach_test; @@ -836,8 +836,8 @@ static void run_plant_and_detach_test(int is_early) /* Activate test with initial breakpoint */ if (!is_early) kgdb_breakpoint(); - probe_kernel_read(after, (char *)kgdbts_break_test, - BREAK_INSTR_SIZE); + copy_from_kernel_nofault(after, (char *)kgdbts_break_test, + BREAK_INSTR_SIZE); if (memcmp(before, after, BREAK_INSTR_SIZE)) { printk(KERN_CRIT "kgdbts: ERROR kgdb corrupted memory\n"); panic("kgdb memory corruption"); diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index efd1a1d1f35e..5d3c691a1c66 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -552,6 +552,8 @@ static int bareudp_validate(struct nlattr *tb[], struct nlattr *data[], static int bareudp2info(struct nlattr *data[], struct bareudp_conf *conf, struct netlink_ext_ack *extack) { + memset(conf, 0, sizeof(*conf)); + if (!data[IFLA_BAREUDP_PORT]) { NL_SET_ERR_MSG(extack, "port not specified"); return -EINVAL; diff --git a/drivers/net/can/peak_canfd/peak_pciefd_main.c b/drivers/net/can/peak_canfd/peak_pciefd_main.c index d08a3d559114..6ad83a881039 100644 --- a/drivers/net/can/peak_canfd/peak_pciefd_main.c +++ b/drivers/net/can/peak_canfd/peak_pciefd_main.c @@ -146,7 +146,7 @@ struct pciefd_rx_dma { __le32 irq_status; __le32 sys_time_low; __le32 sys_time_high; - struct pucan_rx_msg msg[0]; + struct pucan_rx_msg msg[]; } __packed __aligned(4); /* Tx Link record */ @@ -194,7 +194,7 @@ struct pciefd_board { struct pci_dev *pci_dev; int can_count; spinlock_t cmd_lock; /* 64-bits cmds must be atomic */ - struct pciefd_can *can[0]; /* array of network devices */ + struct pciefd_can *can[]; /* array of network devices */ }; /* supported device ids. */ diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index bc0e47c1dbb9..177134596458 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -891,16 +891,16 @@ void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int port, mutex_lock(&ptp_data->lock); - rc = sja1105_ptpclkval_read(priv, &ticks, NULL); + rc = sja1105_ptpegr_ts_poll(ds, port, &ts); if (rc < 0) { - dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc); + dev_err(ds->dev, "timed out polling for tstamp\n"); kfree_skb(skb); goto out; } - rc = sja1105_ptpegr_ts_poll(ds, port, &ts); + rc = sja1105_ptpclkval_read(priv, &ticks, NULL); if (rc < 0) { - dev_err(ds->dev, "timed out polling for tstamp\n"); + dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc); kfree_skb(skb); goto out; } diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index b9b4edb913c1..9b7f1af5f574 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -1249,8 +1249,12 @@ out_disable_adv_intr: static void __alx_stop(struct alx_priv *alx) { - alx_halt(alx); alx_free_irq(alx); + + cancel_work_sync(&alx->link_check_wk); + cancel_work_sync(&alx->reset_wk); + + alx_halt(alx); alx_free_rings(alx); alx_free_napis(alx); } @@ -1855,9 +1859,6 @@ static void alx_remove(struct pci_dev *pdev) struct alx_priv *alx = pci_get_drvdata(pdev); struct alx_hw *hw = &alx->hw; - cancel_work_sync(&alx->link_check_wk); - cancel_work_sync(&alx->reset_wk); - /* restore permanent mac address */ alx_set_macaddr(hw, hw->perm_addr); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index c62589c266b2..b93e05f91d77 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -10037,7 +10037,7 @@ static void bnxt_timer(struct timer_list *t) struct bnxt *bp = from_timer(bp, t, timer); struct net_device *dev = bp->dev; - if (!netif_running(dev)) + if (!netif_running(dev) || !test_bit(BNXT_STATE_OPEN, &bp->state)) return; if (atomic_read(&bp->intr_sem) != 0) @@ -12133,19 +12133,9 @@ static int bnxt_resume(struct device *device) goto resume_exit; } - if (bnxt_hwrm_queue_qportcfg(bp)) { - rc = -ENODEV; + rc = bnxt_hwrm_func_qcaps(bp); + if (rc) goto resume_exit; - } - - if (bp->hwrm_spec_code >= 0x10803) { - if (bnxt_alloc_ctx_mem(bp)) { - rc = -ENODEV; - goto resume_exit; - } - } - if (BNXT_NEW_RM(bp)) - bnxt_hwrm_func_resc_qcaps(bp, false); if (bnxt_hwrm_func_drv_rgtr(bp, NULL, 0, false)) { rc = -ENODEV; @@ -12161,6 +12151,8 @@ static int bnxt_resume(struct device *device) resume_exit: bnxt_ulp_start(bp, rc); + if (!rc) + bnxt_reenable_sriov(bp); rtnl_unlock(); return rc; } @@ -12204,6 +12196,9 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev, bnxt_close(netdev); pci_disable_device(pdev); + bnxt_free_ctx_mem(bp); + kfree(bp->ctx); + bp->ctx = NULL; rtnl_unlock(); /* Request a slot slot reset. */ @@ -12237,12 +12232,16 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) pci_set_master(pdev); err = bnxt_hwrm_func_reset(bp); - if (!err && netif_running(netdev)) - err = bnxt_open(netdev); - - if (!err) - result = PCI_ERS_RESULT_RECOVERED; + if (!err) { + err = bnxt_hwrm_func_qcaps(bp); + if (!err && netif_running(netdev)) + err = bnxt_open(netdev); + } bnxt_ulp_start(bp, err); + if (!err) { + bnxt_reenable_sriov(bp); + result = PCI_ERS_RESULT_RECOVERED; + } } if (result != PCI_ERS_RESULT_RECOVERED) { diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 5b9d7c60eebc..67933079aeea 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -2565,15 +2565,14 @@ static int macb_open(struct net_device *dev) if (bp->ptp_info) bp->ptp_info->ptp_init(dev); + return 0; + napi_exit: for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) napi_disable(&queue->napi); pm_exit: - if (err) { - pm_runtime_put_sync(&bp->pdev->dev); - return err; - } - return 0; + pm_runtime_put_sync(&bp->pdev->dev); + return err; } static int macb_close(struct net_device *dev) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 1b4d04e4474b..2baf7b3ff4cb 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -842,12 +842,13 @@ static int ibmvnic_login(struct net_device *netdev) struct ibmvnic_adapter *adapter = netdev_priv(netdev); unsigned long timeout = msecs_to_jiffies(30000); int retry_count = 0; + int retries = 10; bool retry; int rc; do { retry = false; - if (retry_count > IBMVNIC_MAX_QUEUES) { + if (retry_count > retries) { netdev_warn(netdev, "Login attempts exceeded\n"); return -1; } @@ -862,11 +863,23 @@ static int ibmvnic_login(struct net_device *netdev) if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { - netdev_warn(netdev, "Login timed out\n"); - return -1; + netdev_warn(netdev, "Login timed out, retrying...\n"); + retry = true; + adapter->init_done_rc = 0; + retry_count++; + continue; } - if (adapter->init_done_rc == PARTIALSUCCESS) { + if (adapter->init_done_rc == ABORTED) { + netdev_warn(netdev, "Login aborted, retrying...\n"); + retry = true; + adapter->init_done_rc = 0; + retry_count++; + /* FW or device may be busy, so + * wait a bit before retrying login + */ + msleep(500); + } else if (adapter->init_done_rc == PARTIALSUCCESS) { retry_count++; release_sub_crqs(adapter, 1); diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index d9fa4600f745..4b2de08137be 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -151,10 +151,8 @@ static int e1000_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid); static void e1000_restore_vlan(struct e1000_adapter *adapter); -#ifdef CONFIG_PM -static int e1000_suspend(struct pci_dev *pdev, pm_message_t state); -static int e1000_resume(struct pci_dev *pdev); -#endif +static int __maybe_unused e1000_suspend(struct device *dev); +static int __maybe_unused e1000_resume(struct device *dev); static void e1000_shutdown(struct pci_dev *pdev); #ifdef CONFIG_NET_POLL_CONTROLLER @@ -179,16 +177,16 @@ static const struct pci_error_handlers e1000_err_handler = { .resume = e1000_io_resume, }; +static SIMPLE_DEV_PM_OPS(e1000_pm_ops, e1000_suspend, e1000_resume); + static struct pci_driver e1000_driver = { .name = e1000_driver_name, .id_table = e1000_pci_tbl, .probe = e1000_probe, .remove = e1000_remove, -#ifdef CONFIG_PM - /* Power Management Hooks */ - .suspend = e1000_suspend, - .resume = e1000_resume, -#endif + .driver = { + .pm = &e1000_pm_ops, + }, .shutdown = e1000_shutdown, .err_handler = &e1000_err_handler }; @@ -5060,9 +5058,6 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) struct e1000_hw *hw = &adapter->hw; u32 ctrl, ctrl_ext, rctl, status; u32 wufc = adapter->wol; -#ifdef CONFIG_PM - int retval = 0; -#endif netif_device_detach(netdev); @@ -5076,12 +5071,6 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) e1000_down(adapter); } -#ifdef CONFIG_PM - retval = pci_save_state(pdev); - if (retval) - return retval; -#endif - status = er32(STATUS); if (status & E1000_STATUS_LU) wufc &= ~E1000_WUFC_LNKC; @@ -5142,37 +5131,26 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) return 0; } -#ifdef CONFIG_PM -static int e1000_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused e1000_suspend(struct device *dev) { int retval; + struct pci_dev *pdev = to_pci_dev(dev); bool wake; retval = __e1000_shutdown(pdev, &wake); - if (retval) - return retval; - - if (wake) { - pci_prepare_to_sleep(pdev); - } else { - pci_wake_from_d3(pdev, false); - pci_set_power_state(pdev, PCI_D3hot); - } + device_set_wakeup_enable(dev, wake); - return 0; + return retval; } -static int e1000_resume(struct pci_dev *pdev) +static int __maybe_unused e1000_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; u32 err; - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - pci_save_state(pdev); - if (adapter->need_ioport) err = pci_enable_device(pdev); else @@ -5209,7 +5187,6 @@ static int e1000_resume(struct pci_dev *pdev) return 0; } -#endif static void e1000_shutdown(struct pci_dev *pdev) { diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index a279f4fa9962..6f6479ca1267 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -6349,7 +6349,6 @@ fl_out: pm_runtime_put_sync(netdev->dev.parent); } -#ifdef CONFIG_PM_SLEEP /* S0ix implementation */ static void e1000e_s0ix_entry_flow(struct e1000_adapter *adapter) { @@ -6571,7 +6570,6 @@ static void e1000e_s0ix_exit_flow(struct e1000_adapter *adapter) mac_data &= ~E1000_CTRL_EXT_FORCE_SMBUS; ew32(CTRL_EXT, mac_data); } -#endif /* CONFIG_PM_SLEEP */ static int e1000e_pm_freeze(struct device *dev) { @@ -6611,11 +6609,17 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime) struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - u32 ctrl, ctrl_ext, rctl, status; - /* Runtime suspend should only enable wakeup for link changes */ - u32 wufc = runtime ? E1000_WUFC_LNKC : adapter->wol; + u32 ctrl, ctrl_ext, rctl, status, wufc; int retval = 0; + /* Runtime suspend should only enable wakeup for link changes */ + if (runtime) + wufc = E1000_WUFC_LNKC; + else if (device_may_wakeup(&pdev->dev)) + wufc = adapter->wol; + else + wufc = 0; + status = er32(STATUS); if (status & E1000_STATUS_LU) wufc &= ~E1000_WUFC_LNKC; @@ -6672,7 +6676,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime) if (adapter->hw.phy.type == e1000_phy_igp_3) { e1000e_igp3_phy_powerdown_workaround_ich8lan(&adapter->hw); } else if (hw->mac.type >= e1000_pch_lpt) { - if (!(wufc & (E1000_WUFC_EX | E1000_WUFC_MC | E1000_WUFC_BC))) + if (wufc && !(wufc & (E1000_WUFC_EX | E1000_WUFC_MC | E1000_WUFC_BC))) /* ULP does not support wake from unicast, multicast * or broadcast. */ @@ -6869,7 +6873,6 @@ err_irq: return rc; } -#ifdef CONFIG_PM static int __e1000_resume(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); @@ -6935,8 +6938,7 @@ static int __e1000_resume(struct pci_dev *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int e1000e_pm_suspend(struct device *dev) +static __maybe_unused int e1000e_pm_suspend(struct device *dev) { struct net_device *netdev = pci_get_drvdata(to_pci_dev(dev)); struct e1000_adapter *adapter = netdev_priv(netdev); @@ -6960,7 +6962,7 @@ static int e1000e_pm_suspend(struct device *dev) return rc; } -static int e1000e_pm_resume(struct device *dev) +static __maybe_unused int e1000e_pm_resume(struct device *dev) { struct net_device *netdev = pci_get_drvdata(to_pci_dev(dev)); struct e1000_adapter *adapter = netdev_priv(netdev); @@ -6979,9 +6981,8 @@ static int e1000e_pm_resume(struct device *dev) return e1000e_pm_thaw(dev); } -#endif /* CONFIG_PM_SLEEP */ -static int e1000e_pm_runtime_idle(struct device *dev) +static __maybe_unused int e1000e_pm_runtime_idle(struct device *dev) { struct net_device *netdev = dev_get_drvdata(dev); struct e1000_adapter *adapter = netdev_priv(netdev); @@ -6997,7 +6998,7 @@ static int e1000e_pm_runtime_idle(struct device *dev) return -EBUSY; } -static int e1000e_pm_runtime_resume(struct device *dev) +static __maybe_unused int e1000e_pm_runtime_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); @@ -7014,7 +7015,7 @@ static int e1000e_pm_runtime_resume(struct device *dev) return rc; } -static int e1000e_pm_runtime_suspend(struct device *dev) +static __maybe_unused int e1000e_pm_runtime_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct net_device *netdev = pci_get_drvdata(pdev); @@ -7039,7 +7040,6 @@ static int e1000e_pm_runtime_suspend(struct device *dev) return 0; } -#endif /* CONFIG_PM */ static void e1000_shutdown(struct pci_dev *pdev) { diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 2b5dad2ec650..24f4d8e0da98 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -1544,7 +1544,7 @@ static void mvpp2_read_stats(struct mvpp2_port *port) for (q = 0; q < port->ntxqs; q++) for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_txq_regs); i++) *pstats++ += mvpp2_read_index(port->priv, - MVPP22_CTRS_TX_CTR(port->id, i), + MVPP22_CTRS_TX_CTR(port->id, q), mvpp2_ethtool_txq_regs[i].offset); /* Rxqs are numbered from 0 from the user standpoint, but not from the @@ -1553,7 +1553,7 @@ static void mvpp2_read_stats(struct mvpp2_port *port) for (q = 0; q < port->nrxqs; q++) for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_rxq_regs); i++) *pstats++ += mvpp2_read_index(port->priv, - port->first_rxq + i, + port->first_rxq + q, mvpp2_ethtool_rxq_regs[i].offset); } @@ -5983,8 +5983,8 @@ static int mvpp2_remove(struct platform_device *pdev) { struct mvpp2 *priv = platform_get_drvdata(pdev); struct fwnode_handle *fwnode = pdev->dev.fwnode; + int i = 0, poolnum = MVPP2_BM_POOLS_NUM; struct fwnode_handle *port_fwnode; - int i = 0; mvpp2_dbgfs_cleanup(priv); @@ -5998,7 +5998,10 @@ static int mvpp2_remove(struct platform_device *pdev) destroy_workqueue(priv->stats_queue); - for (i = 0; i < MVPP2_BM_POOLS_NUM; i++) { + if (priv->percpu_pools) + poolnum = mvpp2_get_nrxqs(priv) * 2; + + for (i = 0; i < poolnum; i++) { struct mvpp2_bm_pool *bm_pool = &priv->bm_pools[i]; mvpp2_bm_pool_destroy(&pdev->dev, priv, bm_pool); diff --git a/drivers/net/ethernet/mediatek/mtk_star_emac.c b/drivers/net/ethernet/mediatek/mtk_star_emac.c index f1ace4fec19f..3e765bdcf9e1 100644 --- a/drivers/net/ethernet/mediatek/mtk_star_emac.c +++ b/drivers/net/ethernet/mediatek/mtk_star_emac.c @@ -24,7 +24,6 @@ #include <linux/regmap.h> #include <linux/skbuff.h> #include <linux/spinlock.h> -#include <linux/workqueue.h> #define MTK_STAR_DRVNAME "mtk_star_emac" @@ -262,7 +261,6 @@ struct mtk_star_priv { spinlock_t lock; struct rtnl_link_stats64 stats; - struct work_struct stats_work; }; static struct device *mtk_star_get_dev(struct mtk_star_priv *priv) @@ -432,42 +430,6 @@ static void mtk_star_intr_disable(struct mtk_star_priv *priv) regmap_write(priv->regs, MTK_STAR_REG_INT_MASK, ~0); } -static void mtk_star_intr_enable_tx(struct mtk_star_priv *priv) -{ - regmap_clear_bits(priv->regs, MTK_STAR_REG_INT_MASK, - MTK_STAR_BIT_INT_STS_TNTC); -} - -static void mtk_star_intr_enable_rx(struct mtk_star_priv *priv) -{ - regmap_clear_bits(priv->regs, MTK_STAR_REG_INT_MASK, - MTK_STAR_BIT_INT_STS_FNRC); -} - -static void mtk_star_intr_enable_stats(struct mtk_star_priv *priv) -{ - regmap_clear_bits(priv->regs, MTK_STAR_REG_INT_MASK, - MTK_STAR_REG_INT_STS_MIB_CNT_TH); -} - -static void mtk_star_intr_disable_tx(struct mtk_star_priv *priv) -{ - regmap_set_bits(priv->regs, MTK_STAR_REG_INT_MASK, - MTK_STAR_BIT_INT_STS_TNTC); -} - -static void mtk_star_intr_disable_rx(struct mtk_star_priv *priv) -{ - regmap_set_bits(priv->regs, MTK_STAR_REG_INT_MASK, - MTK_STAR_BIT_INT_STS_FNRC); -} - -static void mtk_star_intr_disable_stats(struct mtk_star_priv *priv) -{ - regmap_set_bits(priv->regs, MTK_STAR_REG_INT_MASK, - MTK_STAR_REG_INT_STS_MIB_CNT_TH); -} - static unsigned int mtk_star_intr_read(struct mtk_star_priv *priv) { unsigned int val; @@ -663,20 +625,6 @@ static void mtk_star_update_stats(struct mtk_star_priv *priv) stats->rx_errors += stats->rx_fifo_errors; } -/* This runs in process context and parallel TX and RX paths executing in - * napi context may result in losing some stats data but this should happen - * seldom enough to be acceptable. - */ -static void mtk_star_update_stats_work(struct work_struct *work) -{ - struct mtk_star_priv *priv = container_of(work, struct mtk_star_priv, - stats_work); - - mtk_star_update_stats(priv); - mtk_star_reset_counters(priv); - mtk_star_intr_enable_stats(priv); -} - static struct sk_buff *mtk_star_alloc_skb(struct net_device *ndev) { uintptr_t tail, offset; @@ -767,42 +715,25 @@ static void mtk_star_free_tx_skbs(struct mtk_star_priv *priv) mtk_star_ring_free_skbs(priv, ring, mtk_star_dma_unmap_tx); } -/* All processing for TX and RX happens in the napi poll callback. */ +/* All processing for TX and RX happens in the napi poll callback. + * + * FIXME: The interrupt handling should be more fine-grained with each + * interrupt enabled/disabled independently when needed. Unfortunatly this + * turned out to impact the driver's stability and until we have something + * working properly, we're disabling all interrupts during TX & RX processing + * or when resetting the counter registers. + */ static irqreturn_t mtk_star_handle_irq(int irq, void *data) { struct mtk_star_priv *priv; struct net_device *ndev; - bool need_napi = false; - unsigned int status; ndev = data; priv = netdev_priv(ndev); if (netif_running(ndev)) { - status = mtk_star_intr_read(priv); - - if (status & MTK_STAR_BIT_INT_STS_TNTC) { - mtk_star_intr_disable_tx(priv); - need_napi = true; - } - - if (status & MTK_STAR_BIT_INT_STS_FNRC) { - mtk_star_intr_disable_rx(priv); - need_napi = true; - } - - if (need_napi) - napi_schedule(&priv->napi); - - /* One of the counters reached 0x8000000 - update stats and - * reset all counters. - */ - if (unlikely(status & MTK_STAR_REG_INT_STS_MIB_CNT_TH)) { - mtk_star_intr_disable_stats(priv); - schedule_work(&priv->stats_work); - } - - mtk_star_intr_ack_all(priv); + mtk_star_intr_disable(priv); + napi_schedule(&priv->napi); } return IRQ_HANDLED; @@ -1169,8 +1100,6 @@ static void mtk_star_tx_complete_all(struct mtk_star_priv *priv) if (wake && netif_queue_stopped(ndev)) netif_wake_queue(ndev); - mtk_star_intr_enable_tx(priv); - spin_unlock(&priv->lock); } @@ -1332,20 +1261,32 @@ static int mtk_star_process_rx(struct mtk_star_priv *priv, int budget) static int mtk_star_poll(struct napi_struct *napi, int budget) { struct mtk_star_priv *priv; + unsigned int status; int received = 0; priv = container_of(napi, struct mtk_star_priv, napi); - /* Clean-up all TX descriptors. */ - mtk_star_tx_complete_all(priv); - /* Receive up to $budget packets. */ - received = mtk_star_process_rx(priv, budget); + status = mtk_star_intr_read(priv); + mtk_star_intr_ack_all(priv); - if (received < budget) { - napi_complete_done(napi, received); - mtk_star_intr_enable_rx(priv); + if (status & MTK_STAR_BIT_INT_STS_TNTC) + /* Clean-up all TX descriptors. */ + mtk_star_tx_complete_all(priv); + + if (status & MTK_STAR_BIT_INT_STS_FNRC) + /* Receive up to $budget packets. */ + received = mtk_star_process_rx(priv, budget); + + if (unlikely(status & MTK_STAR_REG_INT_STS_MIB_CNT_TH)) { + mtk_star_update_stats(priv); + mtk_star_reset_counters(priv); } + if (received < budget) + napi_complete_done(napi, received); + + mtk_star_intr_enable(priv); + return received; } @@ -1532,7 +1473,6 @@ static int mtk_star_probe(struct platform_device *pdev) ndev->max_mtu = MTK_STAR_MAX_FRAME_SIZE; spin_lock_init(&priv->lock); - INIT_WORK(&priv->stats_work, mtk_star_update_stats_work); base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 5ffa32b75e5f..55af877763ed 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -978,8 +978,10 @@ int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu, lossy = !(pfc || pause_en); thres_cells = mlxsw_sp_pg_buf_threshold_get(mlxsw_sp, mtu); + mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, &thres_cells); delay_cells = mlxsw_sp_pg_buf_delay_get(mlxsw_sp, mtu, delay, pfc, pause_en); + mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, &delay_cells); total_cells = thres_cells + delay_cells; taken_headroom_cells += total_cells; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 6f96ca50c9ba..6e87457dd635 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -374,6 +374,19 @@ mlxsw_sp_port_vlan_find_by_vid(const struct mlxsw_sp_port *mlxsw_sp_port, return NULL; } +static inline void +mlxsw_sp_port_headroom_8x_adjust(const struct mlxsw_sp_port *mlxsw_sp_port, + u16 *p_size) +{ + /* Ports with eight lanes use two headroom buffers between which the + * configured headroom size is split. Therefore, multiply the calculated + * headroom size by two. + */ + if (mlxsw_sp_port->mapping.width != 8) + return; + *p_size *= 2; +} + enum mlxsw_sp_flood_type { MLXSW_SP_FLOOD_TYPE_UC, MLXSW_SP_FLOOD_TYPE_BC, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c index 21bfb2f6a6f0..f25a8b084b4b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -312,6 +312,7 @@ static int mlxsw_sp_port_pb_init(struct mlxsw_sp_port *mlxsw_sp_port) if (i == MLXSW_SP_PB_UNUSED) continue; + mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, &size); mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, i, size); } mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c index 304eb8c3d8bd..f843545d3478 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c @@ -782,6 +782,7 @@ mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu) speed = 0; buffsize = mlxsw_sp_span_buffsize_get(mlxsw_sp, speed, mtu); + mlxsw_sp_port_headroom_8x_adjust(mlxsw_sp_port, (u16 *) &buffsize); mlxsw_reg_sbib_pack(sbib_pl, mlxsw_sp_port->local_port, buffsize); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl); } diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index c5c5c688b7e2..f1711ac86d0c 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -3091,6 +3091,8 @@ static const struct pci_device_id lan743x_pcidev_tbl[] = { { 0, } }; +MODULE_DEVICE_TABLE(pci, lan743x_pcidev_tbl); + static struct pci_driver lan743x_pcidev_driver = { .name = DRIVER_NAME, .id_table = lan743x_pcidev_tbl, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 8d7b9bb910f2..10037639ac2c 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -269,7 +269,7 @@ static ssize_t qlcnic_sysfs_read_crb(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); int ret; @@ -286,7 +286,7 @@ static ssize_t qlcnic_sysfs_write_crb(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); int ret; @@ -315,7 +315,7 @@ static ssize_t qlcnic_sysfs_read_mem(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); u64 data; int ret; @@ -337,7 +337,7 @@ static ssize_t qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); u64 data; int ret; @@ -402,7 +402,7 @@ static ssize_t qlcnic_sysfs_write_pm_config(struct file *filp, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_pm_func_cfg *pm_cfg; u32 id, action, pci_func; @@ -452,7 +452,7 @@ static ssize_t qlcnic_sysfs_read_pm_config(struct file *filp, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_pm_func_cfg *pm_cfg; u8 pci_func; @@ -545,7 +545,7 @@ static ssize_t qlcnic_sysfs_write_esw_config(struct file *file, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_esw_func_cfg *esw_cfg; struct qlcnic_npar_info *npar; @@ -629,7 +629,7 @@ static ssize_t qlcnic_sysfs_read_esw_config(struct file *file, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_esw_func_cfg *esw_cfg; u8 pci_func; @@ -681,7 +681,7 @@ static ssize_t qlcnic_sysfs_write_npar_config(struct file *file, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_info nic_info; struct qlcnic_npar_func_cfg *np_cfg; @@ -728,7 +728,7 @@ static ssize_t qlcnic_sysfs_read_npar_config(struct file *file, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_npar_func_cfg *np_cfg; struct qlcnic_info nic_info; @@ -775,7 +775,7 @@ static ssize_t qlcnic_sysfs_get_port_stats(struct file *file, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_esw_statistics port_stats; int ret; @@ -810,7 +810,7 @@ static ssize_t qlcnic_sysfs_get_esw_stats(struct file *file, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_esw_statistics esw_stats; int ret; @@ -845,7 +845,7 @@ static ssize_t qlcnic_sysfs_clear_esw_stats(struct file *file, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); int ret; @@ -875,7 +875,7 @@ static ssize_t qlcnic_sysfs_clear_port_stats(struct file *file, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); int ret; @@ -904,7 +904,7 @@ static ssize_t qlcnic_sysfs_read_pci_config(struct file *file, char *buf, loff_t offset, size_t size) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_pci_func_cfg *pci_cfg; struct qlcnic_pci_info *pci_info; @@ -946,7 +946,7 @@ static ssize_t qlcnic_83xx_sysfs_flash_read_handler(struct file *filp, { unsigned char *p_read_buf; int ret, count; - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); if (!size) @@ -1124,7 +1124,7 @@ static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp, int ret; static int flash_mode; unsigned long data; - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct qlcnic_adapter *adapter = dev_get_drvdata(dev); ret = kstrtoul(buf, 16, &data); diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 7585cd2270ba..fc99e7118e49 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -647,10 +647,10 @@ static int rocker_dma_rings_init(struct rocker *rocker) err_dma_event_ring_bufs_alloc: rocker_dma_ring_destroy(rocker, &rocker->event_ring); err_dma_event_ring_create: + rocker_dma_cmd_ring_waits_free(rocker); +err_dma_cmd_ring_waits_alloc: rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring, PCI_DMA_BIDIRECTIONAL); -err_dma_cmd_ring_waits_alloc: - rocker_dma_cmd_ring_waits_free(rocker); err_dma_cmd_ring_bufs_alloc: rocker_dma_ring_destroy(rocker, &rocker->cmd_ring); return err; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index fbaf3c987d9c..f34c7903ff52 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -186,7 +186,7 @@ #define XAE_RAF_TXVSTRPMODE_MASK 0x00000180 /* Tx VLAN STRIP mode */ #define XAE_RAF_RXVSTRPMODE_MASK 0x00000600 /* Rx VLAN STRIP mode */ #define XAE_RAF_NEWFNCENBL_MASK 0x00000800 /* New function mode */ -/* Exteneded Multicast Filtering mode */ +/* Extended Multicast Filtering mode */ #define XAE_RAF_EMULTIFLTRENBL_MASK 0x00001000 #define XAE_RAF_STATSRST_MASK 0x00002000 /* Stats. Counter Reset */ #define XAE_RAF_RXBADFRMEN_MASK 0x00004000 /* Recv Bad Frame Enable */ diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index ccbb5b43b8b2..4502f9c4708d 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -679,18 +679,8 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n) return a->mode; } - if (a == &dev_attr_align.attr) { - int i; - - for (i = 0; i < nd_region->ndr_mappings; i++) { - struct nd_mapping *nd_mapping = &nd_region->mapping[i]; - struct nvdimm *nvdimm = nd_mapping->nvdimm; - - if (test_bit(NDD_LABELING, &nvdimm->flags)) - return a->mode; - } - return 0; - } + if (a == &dev_attr_align.attr) + return a->mode; if (a != &dev_attr_set_cookie.attr && a != &dev_attr_available_size.attr) diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h index e1d097e250ae..31478c0cff87 100644 --- a/drivers/oprofile/cpu_buffer.h +++ b/drivers/oprofile/cpu_buffer.h @@ -33,7 +33,7 @@ void flush_cpu_work(void); struct op_sample { unsigned long eip; unsigned long event; - unsigned long data[0]; + unsigned long data[]; }; struct op_entry; diff --git a/drivers/phy/samsung/phy-samsung-usb2.h b/drivers/phy/samsung/phy-samsung-usb2.h index 2c1a7d71142b..77fb23bc218f 100644 --- a/drivers/phy/samsung/phy-samsung-usb2.h +++ b/drivers/phy/samsung/phy-samsung-usb2.h @@ -43,7 +43,7 @@ struct samsung_usb2_phy_driver { struct regmap *reg_pmu; struct regmap *reg_sys; spinlock_t lock; - struct samsung_usb2_phy_instance instances[0]; + struct samsung_usb2_phy_instance instances[]; }; struct samsung_usb2_common_phy { diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index cb7e0f08d2cf..1f81569c7ae3 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -824,13 +824,12 @@ int imx_pinctrl_probe(struct platform_device *pdev, return -EINVAL; } - ipctl->input_sel_base = devm_of_iomap(&pdev->dev, np, - 0, NULL); + ipctl->input_sel_base = of_iomap(np, 0); of_node_put(np); - if (IS_ERR(ipctl->input_sel_base)) { + if (!ipctl->input_sel_base) { dev_err(&pdev->dev, "iomuxc input select base address not found\n"); - return PTR_ERR(ipctl->input_sel_base); + return -ENOMEM; } } } diff --git a/drivers/pinctrl/pinctrl-mcp23s08_spi.c b/drivers/pinctrl/pinctrl-mcp23s08_spi.c index e06fb885fd2b..1f47a661b0a7 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08_spi.c +++ b/drivers/pinctrl/pinctrl-mcp23s08_spi.c @@ -126,10 +126,7 @@ static int mcp23s08_spi_regmap_init(struct mcp23s08 *mcp, struct device *dev, copy->name = name; mcp->regmap = devm_regmap_init(dev, &mcp23sxx_spi_regmap, mcp, copy); - if (IS_ERR(mcp->regmap)) - return PTR_ERR(mcp->regmap); - - return 0; + return PTR_ERR_OR_ZERO(mcp->regmap); } static int mcp23s08_probe(struct spi_device *spi) diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 1e0614daee9b..f3a8a465d27e 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -958,7 +958,7 @@ static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, } /** - * smux_parse_one_pinctrl_entry() - parses a device tree mux entry + * pcs_parse_one_pinctrl_entry() - parses a device tree mux entry * @pctldev: pin controller device * @pcs: pinctrl driver instance * @np: device node of the mux entry diff --git a/drivers/pinctrl/qcom/pinctrl-ipq6018.c b/drivers/pinctrl/qcom/pinctrl-ipq6018.c index 38c33a778cb8..ec50a3b4bd16 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq6018.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq6018.c @@ -367,7 +367,8 @@ static const char * const wci20_groups[] = { static const char * const qpic_pad_groups[] = { "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio9", "gpio10", - "gpio11", "gpio17", + "gpio11", "gpio17", "gpio15", "gpio12", "gpio13", "gpio14", "gpio5", + "gpio6", "gpio7", "gpio8", }; static const char * const burn0_groups[] = { diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index fe0be8a6ebb7..092a48e4dff5 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -170,6 +170,7 @@ struct pmic_gpio_state { struct regmap *map; struct pinctrl_dev *ctrl; struct gpio_chip chip; + struct irq_chip irq; }; static const struct pinconf_generic_params pmic_gpio_bindings[] = { @@ -917,16 +918,6 @@ static int pmic_gpio_populate(struct pmic_gpio_state *state, return 0; } -static struct irq_chip pmic_gpio_irq_chip = { - .name = "spmi-gpio", - .irq_ack = irq_chip_ack_parent, - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, - .irq_set_type = irq_chip_set_type_parent, - .irq_set_wake = irq_chip_set_wake_parent, - .flags = IRQCHIP_MASK_ON_SUSPEND, -}; - static int pmic_gpio_domain_translate(struct irq_domain *domain, struct irq_fwspec *fwspec, unsigned long *hwirq, @@ -1053,8 +1044,16 @@ static int pmic_gpio_probe(struct platform_device *pdev) if (!parent_domain) return -ENXIO; + state->irq.name = "spmi-gpio", + state->irq.irq_ack = irq_chip_ack_parent, + state->irq.irq_mask = irq_chip_mask_parent, + state->irq.irq_unmask = irq_chip_unmask_parent, + state->irq.irq_set_type = irq_chip_set_type_parent, + state->irq.irq_set_wake = irq_chip_set_wake_parent, + state->irq.flags = IRQCHIP_MASK_ON_SUSPEND, + girq = &state->chip.irq; - girq->chip = &pmic_gpio_irq_chip; + girq->chip = &state->irq; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_level_irq; girq->fwnode = of_node_to_fwnode(state->dev->of_node); diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index 21661f6490d6..195cfe557511 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -731,8 +731,8 @@ static int tegra_pinctrl_resume(struct device *dev) } const struct dev_pm_ops tegra_pinctrl_pm = { - .suspend = &tegra_pinctrl_suspend, - .resume = &tegra_pinctrl_resume + .suspend_noirq = &tegra_pinctrl_suspend, + .resume_noirq = &tegra_pinctrl_resume }; static bool tegra_pinctrl_gpio_node_has_range(struct tegra_pmx *pmx) diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 0e90c5d4bb2b..eb8ed28533f8 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -39,7 +39,7 @@ struct rio_id_table { u16 start; /* logical minimal id */ u32 max; /* max number of IDs in table */ spinlock_t lock; - unsigned long table[0]; + unsigned long table[]; }; static int next_destid = 0; diff --git a/drivers/reset/reset-intel-gw.c b/drivers/reset/reset-intel-gw.c index 854238444616..effc177db80a 100644 --- a/drivers/reset/reset-intel-gw.c +++ b/drivers/reset/reset-intel-gw.c @@ -15,9 +15,9 @@ #define RCU_RST_STAT 0x0024 #define RCU_RST_REQ 0x0048 -#define REG_OFFSET GENMASK(31, 16) -#define BIT_OFFSET GENMASK(15, 8) -#define STAT_BIT_OFFSET GENMASK(7, 0) +#define REG_OFFSET_MASK GENMASK(31, 16) +#define BIT_OFFSET_MASK GENMASK(15, 8) +#define STAT_BIT_OFFSET_MASK GENMASK(7, 0) #define to_reset_data(x) container_of(x, struct intel_reset_data, rcdev) @@ -51,11 +51,11 @@ static u32 id_to_reg_and_bit_offsets(struct intel_reset_data *data, unsigned long id, u32 *rst_req, u32 *req_bit, u32 *stat_bit) { - *rst_req = FIELD_GET(REG_OFFSET, id); - *req_bit = FIELD_GET(BIT_OFFSET, id); + *rst_req = FIELD_GET(REG_OFFSET_MASK, id); + *req_bit = FIELD_GET(BIT_OFFSET_MASK, id); if (data->soc_data->legacy) - *stat_bit = FIELD_GET(STAT_BIT_OFFSET, id); + *stat_bit = FIELD_GET(STAT_BIT_OFFSET_MASK, id); else *stat_bit = *req_bit; @@ -141,14 +141,14 @@ static int intel_reset_xlate(struct reset_controller_dev *rcdev, if (spec->args[1] > 31) return -EINVAL; - id = FIELD_PREP(REG_OFFSET, spec->args[0]); - id |= FIELD_PREP(BIT_OFFSET, spec->args[1]); + id = FIELD_PREP(REG_OFFSET_MASK, spec->args[0]); + id |= FIELD_PREP(BIT_OFFSET_MASK, spec->args[1]); if (data->soc_data->legacy) { if (spec->args[2] > 31) return -EINVAL; - id |= FIELD_PREP(STAT_BIT_OFFSET, spec->args[2]); + id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, spec->args[2]); } return id; @@ -210,11 +210,11 @@ static int intel_reset_probe(struct platform_device *pdev) if (ret) return ret; - data->reboot_id = FIELD_PREP(REG_OFFSET, rb_id[0]); - data->reboot_id |= FIELD_PREP(BIT_OFFSET, rb_id[1]); + data->reboot_id = FIELD_PREP(REG_OFFSET_MASK, rb_id[0]); + data->reboot_id |= FIELD_PREP(BIT_OFFSET_MASK, rb_id[1]); if (data->soc_data->legacy) - data->reboot_id |= FIELD_PREP(STAT_BIT_OFFSET, rb_id[2]); + data->reboot_id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, rb_id[2]); data->restart_nb.notifier_call = intel_reset_restart_handler; data->restart_nb.priority = 128; diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c index 067e7e7b34f1..e066614818a3 100644 --- a/drivers/reset/reset-simple.c +++ b/drivers/reset/reset-simple.c @@ -11,6 +11,7 @@ * Maxime Ripard <maxime.ripard@free-electrons.com> */ +#include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/io.h> @@ -18,10 +19,9 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/reset-controller.h> +#include <linux/reset/reset-simple.h> #include <linux/spinlock.h> -#include "reset-simple.h" - static inline struct reset_simple_data * to_reset_simple_data(struct reset_controller_dev *rcdev) { @@ -64,6 +64,24 @@ static int reset_simple_deassert(struct reset_controller_dev *rcdev, return reset_simple_update(rcdev, id, false); } +static int reset_simple_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_simple_data *data = to_reset_simple_data(rcdev); + int ret; + + if (!data->reset_us) + return -ENOTSUPP; + + ret = reset_simple_assert(rcdev, id); + if (ret) + return ret; + + usleep_range(data->reset_us, data->reset_us * 2); + + return reset_simple_deassert(rcdev, id); +} + static int reset_simple_status(struct reset_controller_dev *rcdev, unsigned long id) { @@ -81,6 +99,7 @@ static int reset_simple_status(struct reset_controller_dev *rcdev, const struct reset_control_ops reset_simple_ops = { .assert = reset_simple_assert, .deassert = reset_simple_deassert, + .reset = reset_simple_reset, .status = reset_simple_status, }; EXPORT_SYMBOL_GPL(reset_simple_ops); diff --git a/drivers/reset/reset-simple.h b/drivers/reset/reset-simple.h deleted file mode 100644 index 08ccb25a55e6..000000000000 --- a/drivers/reset/reset-simple.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Simple Reset Controller ops - * - * Based on Allwinner SoCs Reset Controller driver - * - * Copyright 2013 Maxime Ripard - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - */ - -#ifndef __RESET_SIMPLE_H__ -#define __RESET_SIMPLE_H__ - -#include <linux/io.h> -#include <linux/reset-controller.h> -#include <linux/spinlock.h> - -/** - * struct reset_simple_data - driver data for simple reset controllers - * @lock: spinlock to protect registers during read-modify-write cycles - * @membase: memory mapped I/O register range - * @rcdev: reset controller device base structure - * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits - * are set to assert the reset. Note that this says nothing about - * the voltage level of the actual reset line. - * @status_active_low: if true, bits read back as cleared while the reset is - * asserted. Otherwise, bits read back as set while the - * reset is asserted. - */ -struct reset_simple_data { - spinlock_t lock; - void __iomem *membase; - struct reset_controller_dev rcdev; - bool active_low; - bool status_active_low; -}; - -extern const struct reset_control_ops reset_simple_ops; - -#endif /* __RESET_SIMPLE_H__ */ diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index 96953992c2bb..bdd984296196 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -11,13 +11,12 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/reset-controller.h> +#include <linux/reset/reset-simple.h> #include <linux/reset/socfpga.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/types.h> -#include "reset-simple.h" - #define SOCFPGA_NR_BANKS 8 static int a10_reset_init(struct device_node *np) diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index e7f169e57bcf..e752594b6971 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c @@ -14,13 +14,12 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/reset-controller.h> +#include <linux/reset/reset-simple.h> #include <linux/reset/sunxi.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/types.h> -#include "reset-simple.h" - static int sunxi_reset_init(struct device_node *np) { struct reset_simple_data *data; diff --git a/drivers/reset/reset-ti-sci.c b/drivers/reset/reset-ti-sci.c index bf68729ab729..b799aefad547 100644 --- a/drivers/reset/reset-ti-sci.c +++ b/drivers/reset/reset-ti-sci.c @@ -1,7 +1,7 @@ /* * Texas Instrument's System Control Interface (TI-SCI) reset driver * - * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis <afd@ti.com> * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c index a2635c21db7f..ef97c4dbbb4e 100644 --- a/drivers/reset/reset-ti-syscon.c +++ b/drivers/reset/reset-ti-syscon.c @@ -1,7 +1,7 @@ /* * TI SYSCON regmap reset driver * - * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis <afd@ti.com> * Suman Anna <afd@ti.com> * diff --git a/drivers/reset/reset-uniphier-glue.c b/drivers/reset/reset-uniphier-glue.c index 2b188b3bb69a..027990b79f61 100644 --- a/drivers/reset/reset-uniphier-glue.c +++ b/drivers/reset/reset-uniphier-glue.c @@ -9,8 +9,7 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/reset.h> - -#include "reset-simple.h" +#include <linux/reset/reset-simple.h> #define MAX_CLKS 2 #define MAX_RSTS 2 diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index eb13c479e11d..bb1c8402c67d 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -182,10 +182,9 @@ enum qdio_irq_poll_states { }; struct qdio_input_q { - /* first ACK'ed buffer */ - int ack_start; - /* how many SBALs are acknowledged */ - int ack_count; + /* Batch of SBALs that we processed while polling the queue: */ + unsigned int batch_start; + unsigned int batch_count; /* last time of noticing incoming data */ u64 timestamp; }; diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 286b044fb027..da95c923d81a 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -110,8 +110,8 @@ static int qstat_show(struct seq_file *m, void *v) seq_printf(m, "nr_used: %d ftc: %d\n", atomic_read(&q->nr_buf_used), q->first_to_check); if (q->is_input_q) { - seq_printf(m, "ack start: %d ack count: %d\n", - q->u.in.ack_start, q->u.in.ack_count); + seq_printf(m, "batch start: %u batch count: %u\n", + q->u.in.batch_start, q->u.in.batch_count); seq_printf(m, "DSCI: %x IRQs disabled: %u\n", *(u8 *)q->irq_ptr->dsci, test_bit(QDIO_IRQ_DISABLED, diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 610c05f59589..0c919a11a46e 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -254,10 +254,17 @@ static inline int set_buf_states(struct qdio_q *q, int bufnr, if (is_qebsm(q)) return qdio_do_sqbs(q, state, bufnr, count); + /* Ensure that all preceding changes to the SBALs are visible: */ + mb(); + for (i = 0; i < count; i++) { - xchg(&q->slsb.val[bufnr], state); + WRITE_ONCE(q->slsb.val[bufnr], state); bufnr = next_buf(bufnr); } + + /* Make our SLSB changes visible: */ + mb(); + return count; } @@ -393,15 +400,15 @@ int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr, static inline void qdio_stop_polling(struct qdio_q *q) { - if (!q->u.in.ack_count) + if (!q->u.in.batch_count) return; qperf_inc(q, stop_polling); /* show the card that we are not polling anymore */ - set_buf_states(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT, - q->u.in.ack_count); - q->u.in.ack_count = 0; + set_buf_states(q, q->u.in.batch_start, SLSB_P_INPUT_NOT_INIT, + q->u.in.batch_count); + q->u.in.batch_count = 0; } static inline void account_sbals(struct qdio_q *q, unsigned int count) @@ -441,42 +448,13 @@ static void process_buffer_error(struct qdio_q *q, unsigned int start, static inline void inbound_handle_work(struct qdio_q *q, unsigned int start, int count, bool auto_ack) { - int new; - - if (auto_ack) { - if (!q->u.in.ack_count) { - q->u.in.ack_count = count; - q->u.in.ack_start = start; - return; - } - - /* delete the previous ACK's */ - set_buf_states(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT, - q->u.in.ack_count); - q->u.in.ack_count = count; - q->u.in.ack_start = start; - return; - } - - /* - * ACK the newest buffer. The ACK will be removed in qdio_stop_polling - * or by the next inbound run. - */ - new = add_buf(start, count - 1); - set_buf_state(q, new, SLSB_P_INPUT_ACK); - - /* delete the previous ACKs */ - if (q->u.in.ack_count) - set_buf_states(q, q->u.in.ack_start, SLSB_P_INPUT_NOT_INIT, - q->u.in.ack_count); + /* ACK the newest SBAL: */ + if (!auto_ack) + set_buf_state(q, add_buf(start, count - 1), SLSB_P_INPUT_ACK); - q->u.in.ack_count = 1; - q->u.in.ack_start = new; - count--; - if (!count) - return; - /* need to change ALL buffers to get more interrupts */ - set_buf_states(q, start, SLSB_P_INPUT_NOT_INIT, count); + if (!q->u.in.batch_count) + q->u.in.batch_start = start; + q->u.in.batch_count += count; } static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start) @@ -525,15 +503,18 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start) account_sbals_error(q, count); return count; case SLSB_CU_INPUT_EMPTY: - case SLSB_P_INPUT_NOT_INIT: - case SLSB_P_INPUT_ACK: if (q->irq_ptr->perf_stat_enabled) q->q_stats.nr_sbal_nop++; DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop:%1d %#02x", q->nr, start); return 0; + case SLSB_P_INPUT_NOT_INIT: + case SLSB_P_INPUT_ACK: + /* We should never see this state, throw a WARN: */ default: - WARN_ON_ONCE(1); + dev_WARN_ONCE(&q->irq_ptr->cdev->dev, 1, + "found state %#x at index %u on queue %u\n", + state, start, q->nr); return 0; } } @@ -738,11 +719,14 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start) DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr); return 0; - case SLSB_P_OUTPUT_NOT_INIT: case SLSB_P_OUTPUT_HALTED: return 0; + case SLSB_P_OUTPUT_NOT_INIT: + /* We should never see this state, throw a WARN: */ default: - WARN_ON_ONCE(1); + dev_WARN_ONCE(&q->irq_ptr->cdev->dev, 1, + "found state %#x at index %u on queue %u\n", + state, start, q->nr); return 0; } } @@ -938,10 +922,10 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) } } -static void qdio_handle_activate_check(struct ccw_device *cdev, - unsigned long intparm, int cstat, int dstat) +static void qdio_handle_activate_check(struct qdio_irq *irq_ptr, + unsigned long intparm, int cstat, + int dstat) { - struct qdio_irq *irq_ptr = cdev->private->qdio_data; struct qdio_q *q; DBF_ERROR("%4x ACT CHECK", irq_ptr->schid.sch_no); @@ -968,11 +952,9 @@ no_handler: lgr_info_log(); } -static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, +static void qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat, int dstat) { - struct qdio_irq *irq_ptr = cdev->private->qdio_data; - DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq"); if (cstat) @@ -1019,7 +1001,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, switch (irq_ptr->state) { case QDIO_IRQ_STATE_INACTIVE: - qdio_establish_handle_irq(cdev, cstat, dstat); + qdio_establish_handle_irq(irq_ptr, cstat, dstat); break; case QDIO_IRQ_STATE_CLEANUP: qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); @@ -1031,7 +1013,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } if (cstat || dstat) - qdio_handle_activate_check(cdev, intparm, cstat, + qdio_handle_activate_check(irq_ptr, intparm, cstat, dstat); break; case QDIO_IRQ_STATE_STOPPED: @@ -1446,12 +1428,12 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags, qperf_inc(q, inbound_call); - /* If any ACKed SBALs are returned to HW, adjust ACK tracking: */ - overlap = min(count - sub_buf(q->u.in.ack_start, bufnr), - q->u.in.ack_count); + /* If any processed SBALs are returned to HW, adjust our tracking: */ + overlap = min_t(int, count - sub_buf(q->u.in.batch_start, bufnr), + q->u.in.batch_count); if (overlap > 0) { - q->u.in.ack_start = add_buf(q->u.in.ack_start, overlap); - q->u.in.ack_count -= overlap; + q->u.in.batch_start = add_buf(q->u.in.batch_start, overlap); + q->u.in.batch_count -= overlap; } count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count); @@ -1535,12 +1517,11 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, int do_QDIO(struct ccw_device *cdev, unsigned int callflags, int q_nr, unsigned int bufnr, unsigned int count) { - struct qdio_irq *irq_ptr; + struct qdio_irq *irq_ptr = cdev->private->qdio_data; if (bufnr >= QDIO_MAX_BUFFERS_PER_Q || count > QDIO_MAX_BUFFERS_PER_Q) return -EINVAL; - irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; diff --git a/drivers/s390/crypto/zcrypt_ep11misc.c b/drivers/s390/crypto/zcrypt_ep11misc.c index 004ce022fc78..3c3d403abe92 100644 --- a/drivers/s390/crypto/zcrypt_ep11misc.c +++ b/drivers/s390/crypto/zcrypt_ep11misc.c @@ -195,11 +195,10 @@ static inline struct ep11_cprb *alloc_cprb(size_t payload_len) size_t len = sizeof(struct ep11_cprb) + payload_len; struct ep11_cprb *cprb; - cprb = kmalloc(len, GFP_KERNEL); + cprb = kzalloc(len, GFP_KERNEL); if (!cprb) return NULL; - memset(cprb, 0, len); cprb->cprb_len = sizeof(struct ep11_cprb); cprb->cprb_ver_id = 0x04; memcpy(cprb->func_id, "T4", 2); diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index 957889a42d2e..5730572b52cd 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -1372,27 +1372,6 @@ static struct ccw_device_id virtio_ids[] = { {}, }; -#ifdef CONFIG_PM_SLEEP -static int virtio_ccw_freeze(struct ccw_device *cdev) -{ - struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); - - return virtio_device_freeze(&vcdev->vdev); -} - -static int virtio_ccw_restore(struct ccw_device *cdev) -{ - struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); - int ret; - - ret = virtio_ccw_set_transport_rev(vcdev); - if (ret) - return ret; - - return virtio_device_restore(&vcdev->vdev); -} -#endif - static struct ccw_driver virtio_ccw_driver = { .driver = { .owner = THIS_MODULE, @@ -1405,11 +1384,6 @@ static struct ccw_driver virtio_ccw_driver = { .set_online = virtio_ccw_online, .notify = virtio_ccw_cio_notify, .int_class = IRQIO_VIR, -#ifdef CONFIG_PM_SLEEP - .freeze = virtio_ccw_freeze, - .thaw = virtio_ccw_restore, - .restore = virtio_ccw_restore, -#endif }; static int __init pure_hex(char **cp, unsigned int *val, int min_digit, diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index d022407e5645..bef47f38dd0d 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -40,6 +40,7 @@ static struct scsi_host_template aic94xx_sht = { /* .name is initialized */ .name = "aic94xx", .queuecommand = sas_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, .scan_finished = asd_scan_finished, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 2e1718f9ade2..09a7669dad4c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1756,6 +1756,7 @@ static struct scsi_host_template sht_v1_hw = { .proc_name = DRV_NAME, .module = THIS_MODULE, .queuecommand = sas_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .target_alloc = sas_target_alloc, .slave_configure = hisi_sas_slave_configure, .scan_finished = hisi_sas_scan_finished, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index e7e7849a4c14..968d38702353 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -3532,6 +3532,7 @@ static struct scsi_host_template sht_v2_hw = { .proc_name = DRV_NAME, .module = THIS_MODULE, .queuecommand = sas_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .target_alloc = sas_target_alloc, .slave_configure = hisi_sas_slave_configure, .scan_finished = hisi_sas_scan_finished, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 3e6b78a1f993..55e2321a65bc 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -3075,6 +3075,7 @@ static struct scsi_host_template sht_v3_hw = { .proc_name = DRV_NAME, .module = THIS_MODULE, .queuecommand = sas_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .target_alloc = sas_target_alloc, .slave_configure = hisi_sas_slave_configure, .scan_finished = hisi_sas_scan_finished, diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 7d77997d26d4..7d86f4ca266c 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -6731,6 +6731,7 @@ static struct scsi_host_template driver_template = { .compat_ioctl = ipr_ioctl, #endif .queuecommand = ipr_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .eh_abort_handler = ipr_eh_abort, .eh_device_reset_handler = ipr_eh_dev_reset, .eh_host_reset_handler = ipr_eh_host_reset, diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 974c3b9116d5..085e285f427d 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -153,6 +153,7 @@ static struct scsi_host_template isci_sht = { .name = DRV_NAME, .proc_name = DRV_NAME, .queuecommand = sas_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, .scan_finished = isci_host_scan_finished, diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 5973eed94938..b0de3bdb01db 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -33,6 +33,7 @@ static struct scsi_host_template mvs_sht = { .module = THIS_MODULE, .name = DRV_NAME, .queuecommand = sas_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, .scan_finished = mvs_scan_finished, diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index a8f5344fdfda..9e99262a2b9d 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -87,6 +87,7 @@ static struct scsi_host_template pm8001_sht = { .module = THIS_MODULE, .name = DRV_NAME, .queuecommand = sas_queuecommand, + .dma_need_drain = ata_scsi_dma_need_drain, .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, .scan_finished = pm8001_scan_finished, diff --git a/drivers/scsi/ufs/ufs_bsg.c b/drivers/scsi/ufs/ufs_bsg.c index 53dd87628cbe..516a7f573942 100644 --- a/drivers/scsi/ufs/ufs_bsg.c +++ b/drivers/scsi/ufs/ufs_bsg.c @@ -106,8 +106,10 @@ static int ufs_bsg_request(struct bsg_job *job) desc_op = bsg_request->upiu_req.qr.opcode; ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff, &desc_len, desc_op); - if (ret) + if (ret) { + pm_runtime_put_sync(hba->dev); goto out; + } /* fall through */ case UPIU_TRANSACTION_NOP_OUT: diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig index d515d2cc20ed..a9370f4aacca 100644 --- a/drivers/soc/imx/Kconfig +++ b/drivers/soc/imx/Kconfig @@ -8,20 +8,12 @@ config IMX_GPCV2_PM_DOMAINS select PM_GENERIC_DOMAINS default y if SOC_IMX7D -config IMX_SCU_SOC - bool "i.MX System Controller Unit SoC info support" - depends on IMX_SCU - select SOC_BUS - help - If you say yes here you get support for the NXP i.MX System - Controller Unit SoC info module, it will provide the SoC info - like SoC family, ID and revision etc. - config SOC_IMX8M bool "i.MX8M SoC family support" depends on ARCH_MXC || COMPILE_TEST default ARCH_MXC && ARM64 select SOC_BUS + select ARM_GIC_V3 if ARCH_MXC help If you say yes here you get support for the NXP i.MX8M family support, it will provide the SoC info like SoC family, diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile index 446143241fe7..078dc918f4f3 100644 --- a/drivers/soc/imx/Makefile +++ b/drivers/soc/imx/Makefile @@ -5,4 +5,3 @@ endif obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o -obj-$(CONFIG_IMX_SCU_SOC) += soc-imx-scu.o diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 87ee9f767b7a..dc644cfb6419 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -12,6 +12,7 @@ #define CMDQ_WRITE_ENABLE_MASK BIT(0) #define CMDQ_POLL_ENABLE_MASK BIT(0) #define CMDQ_EOC_IRQ_EN BIT(0) +#define CMDQ_REG_TYPE 1 struct cmdq_instruction { union { @@ -21,8 +22,17 @@ struct cmdq_instruction { union { u16 offset; u16 event; + u16 reg_dst; + }; + union { + u8 subsys; + struct { + u8 sop:5; + u8 arg_c_t:1; + u8 src_t:1; + u8 dst_t:1; + }; }; - u8 subsys; u8 op; }; @@ -243,6 +253,21 @@ int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event) } EXPORT_SYMBOL(cmdq_pkt_clear_event); +int cmdq_pkt_set_event(struct cmdq_pkt *pkt, u16 event) +{ + struct cmdq_instruction inst = {}; + + if (event >= CMDQ_MAX_EVENT) + return -EINVAL; + + inst.op = CMDQ_CODE_WFE; + inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE; + inst.event = event; + + return cmdq_pkt_append_command(pkt, inst); +} +EXPORT_SYMBOL(cmdq_pkt_set_event); + int cmdq_pkt_poll(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value) { @@ -278,7 +303,19 @@ int cmdq_pkt_poll_mask(struct cmdq_pkt *pkt, u8 subsys, } EXPORT_SYMBOL(cmdq_pkt_poll_mask); -static int cmdq_pkt_finalize(struct cmdq_pkt *pkt) +int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value) +{ + struct cmdq_instruction inst = {}; + + inst.op = CMDQ_CODE_LOGIC; + inst.dst_t = CMDQ_REG_TYPE; + inst.reg_dst = reg_idx; + inst.value = value; + return cmdq_pkt_append_command(pkt, inst); +} +EXPORT_SYMBOL(cmdq_pkt_assign); + +int cmdq_pkt_finalize(struct cmdq_pkt *pkt) { struct cmdq_instruction inst = { {0} }; int err; @@ -297,6 +334,7 @@ static int cmdq_pkt_finalize(struct cmdq_pkt *pkt) return err; } +EXPORT_SYMBOL(cmdq_pkt_finalize); static void cmdq_pkt_flush_async_cb(struct cmdq_cb_data data) { @@ -331,10 +369,6 @@ int cmdq_pkt_flush_async(struct cmdq_pkt *pkt, cmdq_async_flush_cb cb, unsigned long flags = 0; struct cmdq_client *client = (struct cmdq_client *)pkt->cl; - err = cmdq_pkt_finalize(pkt); - if (err < 0) - return err; - pkt->cb.cb = cb; pkt->cb.data = data; pkt->async_cb.cb = cmdq_pkt_flush_async_cb; diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 53cd8d2d0cd2..30984659df90 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -201,6 +201,13 @@ config ARCH_R8A774C0 help This enables support for the Renesas RZ/G2E SoC. +config ARCH_R8A774E1 + bool "Renesas RZ/G2H SoC Platform" + select ARCH_RCAR_GEN3 + select SYSC_R8A774E1 + help + This enables support for the Renesas RZ/G2H SoC. + config ARCH_R8A77950 bool "Renesas R-Car H3 ES1.x SoC Platform" select ARCH_RCAR_GEN3 @@ -296,6 +303,10 @@ config SYSC_R8A774C0 bool "RZ/G2E System Controller support" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A774E1 + bool "RZ/G2H System Controller support" if COMPILE_TEST + select SYSC_RCAR + config SYSC_R8A7779 bool "R-Car H1 System Controller support" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 08296d78e2ad..10a399fc486a 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o obj-$(CONFIG_SYSC_R8A774B1) += r8a774b1-sysc.o obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o +obj-$(CONFIG_SYSC_R8A774E1) += r8a774e1-sysc.o obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o diff --git a/drivers/soc/renesas/r8a774e1-sysc.c b/drivers/soc/renesas/r8a774e1-sysc.c new file mode 100644 index 000000000000..18449f746455 --- /dev/null +++ b/drivers/soc/renesas/r8a774e1-sysc.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2H System Controller + * Copyright (C) 2020 Renesas Electronics Corp. + * + * Based on Renesas R-Car H3 System Controller + * Copyright (C) 2016-2017 Glider bvba + */ + +#include <linux/kernel.h> + +#include <dt-bindings/power/r8a774e1-sysc.h> + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a774e1_areas[] __initconst = { + { "always-on", 0, 0, R8A774E1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A774E1_PD_CA57_SCU, R8A774E1_PD_ALWAYS_ON, PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A774E1_PD_CA57_CPU0, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A774E1_PD_CA57_CPU1, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, + { "ca57-cpu2", 0x80, 2, R8A774E1_PD_CA57_CPU2, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, + { "ca57-cpu3", 0x80, 3, R8A774E1_PD_CA57_CPU3, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, + { "ca53-scu", 0x140, 0, R8A774E1_PD_CA53_SCU, R8A774E1_PD_ALWAYS_ON, PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A774E1_PD_CA53_CPU0, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A774E1_PD_CA53_CPU1, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A774E1_PD_CA53_CPU2, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A774E1_PD_CA53_CPU3, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, + { "a3vp", 0x340, 0, R8A774E1_PD_A3VP, R8A774E1_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A774E1_PD_A3VC, R8A774E1_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A774E1_PD_A2VC1, R8A774E1_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A774E1_PD_3DG_A, R8A774E1_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A774E1_PD_3DG_B, R8A774E1_PD_3DG_A }, + { "3dg-c", 0x100, 2, R8A774E1_PD_3DG_C, R8A774E1_PD_3DG_B }, + { "3dg-d", 0x100, 3, R8A774E1_PD_3DG_D, R8A774E1_PD_3DG_C }, + { "3dg-e", 0x100, 4, R8A774E1_PD_3DG_E, R8A774E1_PD_3DG_D }, +}; + +const struct rcar_sysc_info r8a774e1_sysc_info __initconst = { + .areas = r8a774e1_areas, + .num_areas = ARRAY_SIZE(r8a774e1_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index a2b2b1768768..a932015ce9c1 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -48,6 +48,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a774b1-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a774e1-rst", .data = &rcar_rst_gen3 }, /* R-Car Gen1 */ { .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 }, { .compatible = "renesas,r8a7779-reset-wdt", .data = &rcar_rst_gen1 }, diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 04ea87a188f1..9b235fc90027 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -296,6 +296,9 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A774C0 { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A774E1 + { .compatible = "renesas,r8a774e1-sysc", .data = &r8a774e1_sysc_info }, +#endif #ifdef CONFIG_SYSC_R8A7779 { .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info }, #endif diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index e417f26fe155..8d861c1cfdf7 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -56,6 +56,7 @@ extern const struct rcar_sysc_info r8a77470_sysc_info; extern const struct rcar_sysc_info r8a774a1_sysc_info; extern const struct rcar_sysc_info r8a774b1_sysc_info; extern const struct rcar_sysc_info r8a774c0_sysc_info; +extern const struct rcar_sysc_info r8a774e1_sysc_info; extern const struct rcar_sysc_info r8a7779_sysc_info; extern const struct rcar_sysc_info r8a7790_sysc_info; extern const struct rcar_sysc_info r8a7791_sysc_info; diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 35dba8b8814e..f815a6a8b88b 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -126,6 +126,11 @@ static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = { .id = 0x57, }; +static const struct renesas_soc soc_rz_g2h __initconst __maybe_unused = { + .family = &fam_rzg2, + .id = 0x4f, +}; + static const struct renesas_soc soc_rcar_m1a __initconst __maybe_unused = { .family = &fam_rcar_gen1, }; @@ -238,6 +243,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A774C0 { .compatible = "renesas,r8a774c0", .data = &soc_rz_g2e }, #endif +#ifdef CONFIG_ARCH_R8A774E1 + { .compatible = "renesas,r8a774e1", .data = &soc_rz_g2h }, +#endif #ifdef CONFIG_ARCH_R8A7778 { .compatible = "renesas,r8a7778", .data = &soc_rcar_m1a }, #endif diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index c7a2003687c7..264185664594 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -37,4 +37,7 @@ config EXYNOS_PM_DOMAINS bool "Exynos PM domains" if COMPILE_TEST depends on PM_GENERIC_DOMAINS || COMPILE_TEST +config EXYNOS_REGULATOR_COUPLER + bool "Exynos SoC Regulator Coupler" if COMPILE_TEST + depends on ARCH_EXYNOS || COMPILE_TEST endif diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index edd1d6ea064d..ecc3a32f6406 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \ exynos5250-pmu.o exynos5420-pmu.o obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o +obj-$(CONFIG_EXYNOS_REGULATOR_COUPLER) += exynos-regulator-coupler.o diff --git a/drivers/soc/samsung/exynos-regulator-coupler.c b/drivers/soc/samsung/exynos-regulator-coupler.c new file mode 100644 index 000000000000..61a156b44a48 --- /dev/null +++ b/drivers/soc/samsung/exynos-regulator-coupler.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * Author: Marek Szyprowski <m.szyprowski@samsung.com> + * + * Simplified generic voltage coupler from regulator core.c + * The main difference is that it keeps current regulator voltage + * if consumers didn't apply their constraints yet. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/regulator/coupler.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +static int regulator_get_optimal_voltage(struct regulator_dev *rdev, + int *current_uV, + int *min_uV, int *max_uV, + suspend_state_t state) +{ + struct coupling_desc *c_desc = &rdev->coupling_desc; + struct regulator_dev **c_rdevs = c_desc->coupled_rdevs; + struct regulation_constraints *constraints = rdev->constraints; + int desired_min_uV = 0, desired_max_uV = INT_MAX; + int max_current_uV = 0, min_current_uV = INT_MAX; + int highest_min_uV = 0, target_uV, possible_uV; + int i, ret, max_spread, n_coupled = c_desc->n_coupled; + bool done; + + *current_uV = -1; + + /* Find highest min desired voltage */ + for (i = 0; i < n_coupled; i++) { + int tmp_min = 0; + int tmp_max = INT_MAX; + + lockdep_assert_held_once(&c_rdevs[i]->mutex.base); + + ret = regulator_check_consumers(c_rdevs[i], + &tmp_min, + &tmp_max, state); + if (ret < 0) + return ret; + + if (tmp_min == 0) { + ret = regulator_get_voltage_rdev(c_rdevs[i]); + if (ret < 0) + return ret; + tmp_min = ret; + } + + /* apply constraints */ + ret = regulator_check_voltage(c_rdevs[i], &tmp_min, &tmp_max); + if (ret < 0) + return ret; + + highest_min_uV = max(highest_min_uV, tmp_min); + + if (i == 0) { + desired_min_uV = tmp_min; + desired_max_uV = tmp_max; + } + } + + max_spread = constraints->max_spread[0]; + + /* + * Let target_uV be equal to the desired one if possible. + * If not, set it to minimum voltage, allowed by other coupled + * regulators. + */ + target_uV = max(desired_min_uV, highest_min_uV - max_spread); + + /* + * Find min and max voltages, which currently aren't violating + * max_spread. + */ + for (i = 1; i < n_coupled; i++) { + int tmp_act; + + tmp_act = regulator_get_voltage_rdev(c_rdevs[i]); + if (tmp_act < 0) + return tmp_act; + + min_current_uV = min(tmp_act, min_current_uV); + max_current_uV = max(tmp_act, max_current_uV); + } + + /* + * Correct target voltage, so as it currently isn't + * violating max_spread + */ + possible_uV = max(target_uV, max_current_uV - max_spread); + possible_uV = min(possible_uV, min_current_uV + max_spread); + + if (possible_uV > desired_max_uV) + return -EINVAL; + + done = (possible_uV == target_uV); + desired_min_uV = possible_uV; + + /* Set current_uV if wasn't done earlier in the code and if necessary */ + if (*current_uV == -1) { + ret = regulator_get_voltage_rdev(rdev); + if (ret < 0) + return ret; + *current_uV = ret; + } + + *min_uV = desired_min_uV; + *max_uV = desired_max_uV; + + return done; +} + +static int exynos_coupler_balance_voltage(struct regulator_coupler *coupler, + struct regulator_dev *rdev, + suspend_state_t state) +{ + struct regulator_dev **c_rdevs; + struct regulator_dev *best_rdev; + struct coupling_desc *c_desc = &rdev->coupling_desc; + int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev; + unsigned int delta, best_delta; + unsigned long c_rdev_done = 0; + bool best_c_rdev_done; + + c_rdevs = c_desc->coupled_rdevs; + n_coupled = c_desc->n_coupled; + + /* + * Find the best possible voltage change on each loop. Leave the loop + * if there isn't any possible change. + */ + do { + best_c_rdev_done = false; + best_delta = 0; + best_min_uV = 0; + best_max_uV = 0; + best_c_rdev = 0; + best_rdev = NULL; + + /* + * Find highest difference between optimal voltage + * and current voltage. + */ + for (i = 0; i < n_coupled; i++) { + /* + * optimal_uV is the best voltage that can be set for + * i-th regulator at the moment without violating + * max_spread constraint in order to balance + * the coupled voltages. + */ + int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0; + + if (test_bit(i, &c_rdev_done)) + continue; + + ret = regulator_get_optimal_voltage(c_rdevs[i], + ¤t_uV, + &optimal_uV, + &optimal_max_uV, + state); + if (ret < 0) + goto out; + + delta = abs(optimal_uV - current_uV); + + if (delta && best_delta <= delta) { + best_c_rdev_done = ret; + best_delta = delta; + best_rdev = c_rdevs[i]; + best_min_uV = optimal_uV; + best_max_uV = optimal_max_uV; + best_c_rdev = i; + } + } + + /* Nothing to change, return successfully */ + if (!best_rdev) { + ret = 0; + goto out; + } + + ret = regulator_set_voltage_rdev(best_rdev, best_min_uV, + best_max_uV, state); + + if (ret < 0) + goto out; + + if (best_c_rdev_done) + set_bit(best_c_rdev, &c_rdev_done); + + } while (n_coupled > 1); + +out: + return ret; +} + +static int exynos_coupler_attach(struct regulator_coupler *coupler, + struct regulator_dev *rdev) +{ + return 0; +} + +static struct regulator_coupler exynos_coupler = { + .attach_regulator = exynos_coupler_attach, + .balance_voltage = exynos_coupler_balance_voltage, +}; + +static int __init exynos_coupler_init(void) +{ + if (!of_machine_is_compatible("samsung,exynos5800")) + return 0; + + return regulator_coupler_register(&exynos_coupler); +} +arch_initcall(exynos_coupler_init); diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index 3cdd69d1bd4d..8e416ad91ee2 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -27,7 +27,7 @@ static u32 chipid; u32 tegra_read_chipid(void) { - WARN(!chipid, "Tegra ABP MISC not yet available\n"); + WARN(!chipid, "Tegra APB MISC not yet available\n"); return chipid; } diff --git a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c index 5fb2ee2ac978..6dcc21dde0cb 100644 --- a/drivers/soc/ti/k3-ringacc.c +++ b/drivers/soc/ti/k3-ringacc.c @@ -109,6 +109,21 @@ struct k3_ring_ops { }; /** + * struct k3_ring_state - Internal state tracking structure + * + * @free: Number of free entries + * @occ: Occupancy + * @windex: Write index + * @rindex: Read index + */ +struct k3_ring_state { + u32 free; + u32 occ; + u32 windex; + u32 rindex; +}; + +/** * struct k3_ring - RA Ring descriptor * * @rt: Ring control/status registers @@ -121,10 +136,6 @@ struct k3_ring_ops { * @elm_size: Size of the ring element * @mode: Ring mode * @flags: flags - * @free: Number of free elements - * @occ: Ring occupancy - * @windex: Write index (only for @K3_RINGACC_RING_MODE_RING) - * @rindex: Read index (only for @K3_RINGACC_RING_MODE_RING) * @ring_id: Ring Id * @parent: Pointer on struct @k3_ringacc * @use_count: Use count for shared rings @@ -143,16 +154,17 @@ struct k3_ring { u32 flags; #define K3_RING_FLAG_BUSY BIT(1) #define K3_RING_FLAG_SHARED BIT(2) - u32 free; - u32 occ; - u32 windex; - u32 rindex; + struct k3_ring_state state; u32 ring_id; struct k3_ringacc *parent; u32 use_count; int proxy_id; }; +struct k3_ringacc_ops { + int (*init)(struct platform_device *pdev, struct k3_ringacc *ringacc); +}; + /** * struct k3_ringacc - Rings accelerator descriptor * @@ -171,6 +183,7 @@ struct k3_ring { * @tisci: pointer ti-sci handle * @tisci_ring_ops: ti-sci rings ops * @tisci_dev_id: ti-sci device id + * @ops: SoC specific ringacc operation */ struct k3_ringacc { struct device *dev; @@ -191,6 +204,8 @@ struct k3_ringacc { const struct ti_sci_handle *tisci; const struct ti_sci_rm_ringacc_ops *tisci_ring_ops; u32 tisci_dev_id; + + const struct k3_ringacc_ops *ops; }; static long k3_ringacc_ring_get_fifo_pos(struct k3_ring *ring) @@ -245,6 +260,7 @@ static void k3_ringacc_ring_dump(struct k3_ring *ring) &ring->ring_mem_dma); dev_dbg(dev, "dump elmsize %d, size %d, mode %d, proxy_id %d\n", ring->elm_size, ring->size, ring->mode, ring->proxy_id); + dev_dbg(dev, "dump flags %08X\n", ring->flags); dev_dbg(dev, "dump ring_rt_regs: db%08x\n", readl(&ring->rt->db)); dev_dbg(dev, "dump occ%08x\n", readl(&ring->rt->occ)); @@ -313,6 +329,30 @@ error: } EXPORT_SYMBOL_GPL(k3_ringacc_request_ring); +int k3_ringacc_request_rings_pair(struct k3_ringacc *ringacc, + int fwd_id, int compl_id, + struct k3_ring **fwd_ring, + struct k3_ring **compl_ring) +{ + int ret = 0; + + if (!fwd_ring || !compl_ring) + return -EINVAL; + + *fwd_ring = k3_ringacc_request_ring(ringacc, fwd_id, 0); + if (!(*fwd_ring)) + return -ENODEV; + + *compl_ring = k3_ringacc_request_ring(ringacc, compl_id, 0); + if (!(*compl_ring)) { + k3_ringacc_ring_free(*fwd_ring); + ret = -ENODEV; + } + + return ret; +} +EXPORT_SYMBOL_GPL(k3_ringacc_request_rings_pair); + static void k3_ringacc_ring_reset_sci(struct k3_ring *ring) { struct k3_ringacc *ringacc = ring->parent; @@ -339,10 +379,7 @@ void k3_ringacc_ring_reset(struct k3_ring *ring) if (!ring || !(ring->flags & K3_RING_FLAG_BUSY)) return; - ring->occ = 0; - ring->free = 0; - ring->rindex = 0; - ring->windex = 0; + memset(&ring->state, 0, sizeof(ring->state)); k3_ringacc_ring_reset_sci(ring); } @@ -556,11 +593,13 @@ static int k3_ringacc_ring_cfg_sci(struct k3_ring *ring) int k3_ringacc_ring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg) { - struct k3_ringacc *ringacc = ring->parent; + struct k3_ringacc *ringacc; int ret = 0; if (!ring || !cfg) return -EINVAL; + ringacc = ring->parent; + if (cfg->elm_size > K3_RINGACC_RING_ELSIZE_256 || cfg->mode >= K3_RINGACC_RING_MODE_INVALID || cfg->size & ~K3_RINGACC_CFG_RING_SIZE_ELCNT_MASK || @@ -590,10 +629,7 @@ int k3_ringacc_ring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg) ring->size = cfg->size; ring->elm_size = cfg->elm_size; ring->mode = cfg->mode; - ring->occ = 0; - ring->free = 0; - ring->rindex = 0; - ring->windex = 0; + memset(&ring->state, 0, sizeof(ring->state)); if (ring->proxy_id != K3_RINGACC_PROXY_NOT_USED) ring->proxy = ringacc->proxy_target_base + @@ -613,7 +649,7 @@ int k3_ringacc_ring_cfg(struct k3_ring *ring, struct k3_ring_cfg *cfg) ring->ops = NULL; ret = -EINVAL; goto err_free_proxy; - }; + } ring->ring_mem_virt = dma_alloc_coherent(ringacc->dev, ring->size * (4 << ring->elm_size), @@ -664,10 +700,10 @@ u32 k3_ringacc_ring_get_free(struct k3_ring *ring) if (!ring || !(ring->flags & K3_RING_FLAG_BUSY)) return -EINVAL; - if (!ring->free) - ring->free = ring->size - readl(&ring->rt->occ); + if (!ring->state.free) + ring->state.free = ring->size - readl(&ring->rt->occ); - return ring->free; + return ring->state.free; } EXPORT_SYMBOL_GPL(k3_ringacc_ring_get_free); @@ -738,7 +774,7 @@ static int k3_ringacc_ring_access_proxy(struct k3_ring *ring, void *elem, "proxy:memcpy_fromio(x): --> ptr(%p), mode:%d\n", ptr, access_mode); memcpy_fromio(elem, ptr, (4 << ring->elm_size)); - ring->occ--; + ring->state.occ--; break; case K3_RINGACC_ACCESS_MODE_PUSH_TAIL: case K3_RINGACC_ACCESS_MODE_PUSH_HEAD: @@ -746,14 +782,14 @@ static int k3_ringacc_ring_access_proxy(struct k3_ring *ring, void *elem, "proxy:memcpy_toio(x): --> ptr(%p), mode:%d\n", ptr, access_mode); memcpy_toio(ptr, elem, (4 << ring->elm_size)); - ring->free--; + ring->state.free--; break; default: return -EINVAL; } - dev_dbg(ring->parent->dev, "proxy: free%d occ%d\n", ring->free, - ring->occ); + dev_dbg(ring->parent->dev, "proxy: free%d occ%d\n", ring->state.free, + ring->state.occ); return 0; } @@ -808,7 +844,7 @@ static int k3_ringacc_ring_access_io(struct k3_ring *ring, void *elem, "memcpy_fromio(x): --> ptr(%p), mode:%d\n", ptr, access_mode); memcpy_fromio(elem, ptr, (4 << ring->elm_size)); - ring->occ--; + ring->state.occ--; break; case K3_RINGACC_ACCESS_MODE_PUSH_TAIL: case K3_RINGACC_ACCESS_MODE_PUSH_HEAD: @@ -816,14 +852,15 @@ static int k3_ringacc_ring_access_io(struct k3_ring *ring, void *elem, "memcpy_toio(x): --> ptr(%p), mode:%d\n", ptr, access_mode); memcpy_toio(ptr, elem, (4 << ring->elm_size)); - ring->free--; + ring->state.free--; break; default: return -EINVAL; } - dev_dbg(ring->parent->dev, "free%d index%d occ%d index%d\n", ring->free, - ring->windex, ring->occ, ring->rindex); + dev_dbg(ring->parent->dev, "free%d index%d occ%d index%d\n", + ring->state.free, ring->state.windex, ring->state.occ, + ring->state.rindex); return 0; } @@ -855,16 +892,16 @@ static int k3_ringacc_ring_push_mem(struct k3_ring *ring, void *elem) { void *elem_ptr; - elem_ptr = k3_ringacc_get_elm_addr(ring, ring->windex); + elem_ptr = k3_ringacc_get_elm_addr(ring, ring->state.windex); memcpy(elem_ptr, elem, (4 << ring->elm_size)); - ring->windex = (ring->windex + 1) % ring->size; - ring->free--; + ring->state.windex = (ring->state.windex + 1) % ring->size; + ring->state.free--; writel(1, &ring->rt->db); dev_dbg(ring->parent->dev, "ring_push_mem: free%d index%d\n", - ring->free, ring->windex); + ring->state.free, ring->state.windex); return 0; } @@ -873,16 +910,16 @@ static int k3_ringacc_ring_pop_mem(struct k3_ring *ring, void *elem) { void *elem_ptr; - elem_ptr = k3_ringacc_get_elm_addr(ring, ring->rindex); + elem_ptr = k3_ringacc_get_elm_addr(ring, ring->state.rindex); memcpy(elem, elem_ptr, (4 << ring->elm_size)); - ring->rindex = (ring->rindex + 1) % ring->size; - ring->occ--; + ring->state.rindex = (ring->state.rindex + 1) % ring->size; + ring->state.occ--; writel(-1, &ring->rt->db); dev_dbg(ring->parent->dev, "ring_pop_mem: occ%d index%d pos_ptr%p\n", - ring->occ, ring->rindex, elem_ptr); + ring->state.occ, ring->state.rindex, elem_ptr); return 0; } @@ -893,8 +930,8 @@ int k3_ringacc_ring_push(struct k3_ring *ring, void *elem) if (!ring || !(ring->flags & K3_RING_FLAG_BUSY)) return -EINVAL; - dev_dbg(ring->parent->dev, "ring_push: free%d index%d\n", ring->free, - ring->windex); + dev_dbg(ring->parent->dev, "ring_push: free%d index%d\n", + ring->state.free, ring->state.windex); if (k3_ringacc_ring_is_full(ring)) return -ENOMEM; @@ -914,7 +951,7 @@ int k3_ringacc_ring_push_head(struct k3_ring *ring, void *elem) return -EINVAL; dev_dbg(ring->parent->dev, "ring_push_head: free%d index%d\n", - ring->free, ring->windex); + ring->state.free, ring->state.windex); if (k3_ringacc_ring_is_full(ring)) return -ENOMEM; @@ -933,13 +970,13 @@ int k3_ringacc_ring_pop(struct k3_ring *ring, void *elem) if (!ring || !(ring->flags & K3_RING_FLAG_BUSY)) return -EINVAL; - if (!ring->occ) - ring->occ = k3_ringacc_ring_get_occ(ring); + if (!ring->state.occ) + ring->state.occ = k3_ringacc_ring_get_occ(ring); - dev_dbg(ring->parent->dev, "ring_pop: occ%d index%d\n", ring->occ, - ring->rindex); + dev_dbg(ring->parent->dev, "ring_pop: occ%d index%d\n", ring->state.occ, + ring->state.rindex); - if (!ring->occ) + if (!ring->state.occ) return -ENODATA; if (ring->ops && ring->ops->pop_head) @@ -956,13 +993,13 @@ int k3_ringacc_ring_pop_tail(struct k3_ring *ring, void *elem) if (!ring || !(ring->flags & K3_RING_FLAG_BUSY)) return -EINVAL; - if (!ring->occ) - ring->occ = k3_ringacc_ring_get_occ(ring); + if (!ring->state.occ) + ring->state.occ = k3_ringacc_ring_get_occ(ring); - dev_dbg(ring->parent->dev, "ring_pop_tail: occ%d index%d\n", ring->occ, - ring->rindex); + dev_dbg(ring->parent->dev, "ring_pop_tail: occ%d index%d\n", + ring->state.occ, ring->state.rindex); - if (!ring->occ) + if (!ring->state.occ) return -ENODATA; if (ring->ops && ring->ops->pop_tail) @@ -1047,21 +1084,14 @@ static int k3_ringacc_probe_dt(struct k3_ringacc *ringacc) ringacc->rm_gp_range); } -static int k3_ringacc_probe(struct platform_device *pdev) +static int k3_ringacc_init(struct platform_device *pdev, + struct k3_ringacc *ringacc) { - struct k3_ringacc *ringacc; void __iomem *base_fifo, *base_rt; struct device *dev = &pdev->dev; struct resource *res; int ret, i; - ringacc = devm_kzalloc(dev, sizeof(*ringacc), GFP_KERNEL); - if (!ringacc) - return -ENOMEM; - - ringacc->dev = dev; - mutex_init(&ringacc->req_lock); - dev->msi_domain = of_msi_get_domain(dev, dev->of_node, DOMAIN_BUS_TI_SCI_INTA_MSI); if (!dev->msi_domain) { @@ -1120,14 +1150,9 @@ static int k3_ringacc_probe(struct platform_device *pdev) ringacc->rings[i].ring_id = i; ringacc->rings[i].proxy_id = K3_RINGACC_PROXY_NOT_USED; } - dev_set_drvdata(dev, ringacc); ringacc->tisci_ring_ops = &ringacc->tisci->ops.rm_ring_ops; - mutex_lock(&k3_ringacc_list_lock); - list_add_tail(&ringacc->list, &k3_ringacc_list); - mutex_unlock(&k3_ringacc_list_lock); - dev_info(dev, "Ring Accelerator probed rings:%u, gp-rings[%u,%u] sci-dev-id:%u\n", ringacc->num_rings, ringacc->rm_gp_range->desc[0].start, @@ -1137,15 +1162,60 @@ static int k3_ringacc_probe(struct platform_device *pdev) ringacc->dma_ring_reset_quirk ? "enabled" : "disabled"); dev_info(dev, "RA Proxy rev. %08x, num_proxies:%u\n", readl(&ringacc->proxy_gcfg->revision), ringacc->num_proxies); + return 0; } +struct ringacc_match_data { + struct k3_ringacc_ops ops; +}; + +static struct ringacc_match_data k3_ringacc_data = { + .ops = { + .init = k3_ringacc_init, + }, +}; + /* Match table for of_platform binding */ static const struct of_device_id k3_ringacc_of_match[] = { - { .compatible = "ti,am654-navss-ringacc", }, + { .compatible = "ti,am654-navss-ringacc", .data = &k3_ringacc_data, }, {}, }; +static int k3_ringacc_probe(struct platform_device *pdev) +{ + const struct ringacc_match_data *match_data; + const struct of_device_id *match; + struct device *dev = &pdev->dev; + struct k3_ringacc *ringacc; + int ret; + + match = of_match_node(k3_ringacc_of_match, dev->of_node); + if (!match) + return -ENODEV; + match_data = match->data; + + ringacc = devm_kzalloc(dev, sizeof(*ringacc), GFP_KERNEL); + if (!ringacc) + return -ENOMEM; + + ringacc->dev = dev; + mutex_init(&ringacc->req_lock); + ringacc->ops = &match_data->ops; + + ret = ringacc->ops->init(pdev, ringacc); + if (ret) + return ret; + + dev_set_drvdata(dev, ringacc); + + mutex_lock(&k3_ringacc_list_lock); + list_add_tail(&ringacc->list, &k3_ringacc_list); + mutex_unlock(&k3_ringacc_list_lock); + + return 0; +} + static struct platform_driver k3_ringacc_driver = { .probe = k3_ringacc_probe, .driver = { diff --git a/drivers/soc/ti/knav_qmss.h b/drivers/soc/ti/knav_qmss.h index 038aec352df7..a01eda720bf6 100644 --- a/drivers/soc/ti/knav_qmss.h +++ b/drivers/soc/ti/knav_qmss.h @@ -67,7 +67,7 @@ struct knav_reg_config { u32 link_ram_size0; u32 link_ram_base1; u32 __pad2[2]; - u32 starvation[0]; + u32 starvation[]; }; struct knav_reg_region { diff --git a/drivers/soc/ti/knav_qmss_acc.c b/drivers/soc/ti/knav_qmss_acc.c index 1762d89fc05d..fde66e28e046 100644 --- a/drivers/soc/ti/knav_qmss_acc.c +++ b/drivers/soc/ti/knav_qmss_acc.c @@ -450,7 +450,7 @@ static int knav_acc_free_range(struct knav_range_info *range) return 0; } -struct knav_range_ops knav_acc_range_ops = { +static struct knav_range_ops knav_acc_range_ops = { .set_notify = knav_acc_set_notify, .init_queue = knav_acc_init_queue, .open_queue = knav_acc_open_queue, diff --git a/drivers/soc/ux500/ux500-soc-id.c b/drivers/soc/ux500/ux500-soc-id.c index d64feeb51a40..a9472e0e5d61 100644 --- a/drivers/soc/ux500/ux500-soc-id.c +++ b/drivers/soc/ux500/ux500-soc-id.c @@ -146,9 +146,8 @@ static const char * __init ux500_get_revision(void) return kasprintf(GFP_KERNEL, "%s", "Unknown"); } -static ssize_t ux500_get_process(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +process_show(struct device *dev, struct device_attribute *attr, char *buf) { if (dbx500_id.process == 0x00) return sprintf(buf, "Standard\n"); @@ -156,6 +155,15 @@ static ssize_t ux500_get_process(struct device *dev, return sprintf(buf, "%02xnm\n", dbx500_id.process); } +static DEVICE_ATTR_RO(process); + +static struct attribute *ux500_soc_attrs[] = { + &dev_attr_process.attr, + NULL +}; + +ATTRIBUTE_GROUPS(ux500_soc); + static const char *db8500_read_soc_id(struct device_node *backupram) { void __iomem *base; @@ -184,14 +192,11 @@ static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr, soc_dev_attr->machine = ux500_get_machine(); soc_dev_attr->family = ux500_get_family(); soc_dev_attr->revision = ux500_get_revision(); + soc_dev_attr->custom_attr_group = ux500_soc_groups[0]; } -static const struct device_attribute ux500_soc_attr = - __ATTR(process, S_IRUGO, ux500_get_process, NULL); - static int __init ux500_soc_device_init(void) { - struct device *parent; struct soc_device *soc_dev; struct soc_device_attribute *soc_dev_attr; struct device_node *backupram; @@ -217,9 +222,6 @@ static int __init ux500_soc_device_init(void) return PTR_ERR(soc_dev); } - parent = soc_device_to_device(soc_dev); - device_create_file(parent, &ux500_soc_attr); - return 0; } subsys_initcall(ux500_soc_device_init); diff --git a/drivers/soc/versatile/soc-integrator.c b/drivers/soc/versatile/soc-integrator.c index ae13fa2aa582..7dcf77ccd31e 100644 --- a/drivers/soc/versatile/soc-integrator.c +++ b/drivers/soc/versatile/soc-integrator.c @@ -56,45 +56,47 @@ static const char *integrator_fpga_str(u32 id) } } -static ssize_t integrator_get_manf(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +manufacturer_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%02x\n", integrator_coreid >> 24); } -static struct device_attribute integrator_manf_attr = - __ATTR(manufacturer, S_IRUGO, integrator_get_manf, NULL); +static DEVICE_ATTR_RO(manufacturer); -static ssize_t integrator_get_arch(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +arch_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", integrator_arch_str(integrator_coreid)); } -static struct device_attribute integrator_arch_attr = - __ATTR(arch, S_IRUGO, integrator_get_arch, NULL); +static DEVICE_ATTR_RO(arch); -static ssize_t integrator_get_fpga(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +fpga_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", integrator_fpga_str(integrator_coreid)); } -static struct device_attribute integrator_fpga_attr = - __ATTR(fpga, S_IRUGO, integrator_get_fpga, NULL); +static DEVICE_ATTR_RO(fpga); -static ssize_t integrator_get_build(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +build_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%02x\n", (integrator_coreid >> 4) & 0xFF); } -static struct device_attribute integrator_build_attr = - __ATTR(build, S_IRUGO, integrator_get_build, NULL); +static DEVICE_ATTR_RO(build); + +static struct attribute *integrator_attrs[] = { + &dev_attr_manufacturer.attr, + &dev_attr_arch.attr, + &dev_attr_fpga.attr, + &dev_attr_build.attr, + NULL +}; + +ATTRIBUTE_GROUPS(integrator); static int __init integrator_soc_init(void) { @@ -127,6 +129,7 @@ static int __init integrator_soc_init(void) soc_dev_attr->soc_id = "Integrator"; soc_dev_attr->machine = "Integrator"; soc_dev_attr->family = "Versatile"; + soc_dev_attr->custom_attr_group = integrator_groups[0]; soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { kfree(soc_dev_attr); @@ -134,11 +137,6 @@ static int __init integrator_soc_init(void) } dev = soc_device_to_device(soc_dev); - device_create_file(dev, &integrator_manf_attr); - device_create_file(dev, &integrator_arch_attr); - device_create_file(dev, &integrator_fpga_attr); - device_create_file(dev, &integrator_build_attr); - dev_info(dev, "Detected ARM core module:\n"); dev_info(dev, " Manufacturer: %02x\n", (val >> 24)); dev_info(dev, " Architecture: %s\n", integrator_arch_str(val)); diff --git a/drivers/soc/versatile/soc-realview.c b/drivers/soc/versatile/soc-realview.c index 9471353dd8c3..c6876d232d8f 100644 --- a/drivers/soc/versatile/soc-realview.c +++ b/drivers/soc/versatile/soc-realview.c @@ -39,45 +39,47 @@ static const char *realview_arch_str(u32 id) } } -static ssize_t realview_get_manf(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +manufacturer_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%02x\n", realview_coreid >> 24); } -static struct device_attribute realview_manf_attr = - __ATTR(manufacturer, S_IRUGO, realview_get_manf, NULL); +static DEVICE_ATTR_RO(manufacturer); -static ssize_t realview_get_board(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +board_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "HBI-%03x\n", ((realview_coreid >> 16) & 0xfff)); } -static struct device_attribute realview_board_attr = - __ATTR(board, S_IRUGO, realview_get_board, NULL); +static DEVICE_ATTR_RO(board); -static ssize_t realview_get_arch(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +fpga_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", realview_arch_str(realview_coreid)); } -static struct device_attribute realview_arch_attr = - __ATTR(fpga, S_IRUGO, realview_get_arch, NULL); +static DEVICE_ATTR_RO(fpga); -static ssize_t realview_get_build(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t +build_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%02x\n", (realview_coreid & 0xFF)); } -static struct device_attribute realview_build_attr = - __ATTR(build, S_IRUGO, realview_get_build, NULL); +static DEVICE_ATTR_RO(build); + +static struct attribute *realview_attrs[] = { + &dev_attr_manufacturer.attr, + &dev_attr_board.attr, + &dev_attr_fpga.attr, + &dev_attr_build.attr, + NULL +}; + +ATTRIBUTE_GROUPS(realview); static int realview_soc_probe(struct platform_device *pdev) { @@ -102,6 +104,7 @@ static int realview_soc_probe(struct platform_device *pdev) soc_dev_attr->machine = "RealView"; soc_dev_attr->family = "Versatile"; + soc_dev_attr->custom_attr_group = realview_groups[0]; soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { kfree(soc_dev_attr); @@ -112,11 +115,6 @@ static int realview_soc_probe(struct platform_device *pdev) if (ret) return -ENODEV; - device_create_file(soc_device_to_device(soc_dev), &realview_manf_attr); - device_create_file(soc_device_to_device(soc_dev), &realview_board_attr); - device_create_file(soc_device_to_device(soc_dev), &realview_arch_attr); - device_create_file(soc_device_to_device(soc_dev), &realview_build_attr); - dev_info(&pdev->dev, "RealView Syscon Core ID: 0x%08x, HBI-%03x\n", realview_coreid, ((realview_coreid >> 16) & 0xfff)); diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 99698b8a3a74..b373b1b08b6d 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -17,6 +17,7 @@ #include <linux/tee_drv.h> #include <linux/types.h> #include <linux/uaccess.h> +#include <linux/workqueue.h> #include "optee_private.h" #include "optee_smc.h" #include "shm_pool.h" @@ -218,6 +219,11 @@ static void optee_get_version(struct tee_device *teedev, *vers = v; } +static void optee_bus_scan(struct work_struct *work) +{ + WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); +} + static int optee_open(struct tee_context *ctx) { struct optee_context_data *ctxdata; @@ -241,8 +247,18 @@ static int optee_open(struct tee_context *ctx) kfree(ctxdata); return -EBUSY; } - } + if (!optee->scan_bus_done) { + INIT_WORK(&optee->scan_bus_work, optee_bus_scan); + optee->scan_bus_wq = create_workqueue("optee_bus_scan"); + if (!optee->scan_bus_wq) { + kfree(ctxdata); + return -ECHILD; + } + queue_work(optee->scan_bus_wq, &optee->scan_bus_work); + optee->scan_bus_done = true; + } + } mutex_init(&ctxdata->mutex); INIT_LIST_HEAD(&ctxdata->sess_list); @@ -296,8 +312,13 @@ static void optee_release(struct tee_context *ctx) ctx->data = NULL; - if (teedev == optee->supp_teedev) + if (teedev == optee->supp_teedev) { + if (optee->scan_bus_wq) { + destroy_workqueue(optee->scan_bus_wq); + optee->scan_bus_wq = NULL; + } optee_supp_release(&optee->supp); + } } static const struct tee_driver_ops optee_ops = { @@ -675,7 +696,7 @@ static int optee_probe(struct platform_device *pdev) platform_set_drvdata(pdev, optee); - rc = optee_enumerate_devices(); + rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES); if (rc) { optee_remove(pdev); return rc; diff --git a/drivers/tee/optee/device.c b/drivers/tee/optee/device.c index e3a148521ec1..7a897d51969f 100644 --- a/drivers/tee/optee/device.c +++ b/drivers/tee/optee/device.c @@ -11,18 +11,6 @@ #include <linux/uuid.h> #include "optee_private.h" -/* - * Get device UUIDs - * - * [out] memref[0] Array of device UUIDs - * - * Return codes: - * TEE_SUCCESS - Invoke command success - * TEE_ERROR_BAD_PARAMETERS - Incorrect input param - * TEE_ERROR_SHORT_BUFFER - Output buffer size less than required - */ -#define PTA_CMD_GET_DEVICES 0x0 - static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) { if (ver->impl_id == TEE_IMPL_ID_OPTEE) @@ -32,7 +20,8 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) } static int get_devices(struct tee_context *ctx, u32 session, - struct tee_shm *device_shm, u32 *shm_size) + struct tee_shm *device_shm, u32 *shm_size, + u32 func) { int ret = 0; struct tee_ioctl_invoke_arg inv_arg; @@ -41,8 +30,7 @@ static int get_devices(struct tee_context *ctx, u32 session, memset(&inv_arg, 0, sizeof(inv_arg)); memset(¶m, 0, sizeof(param)); - /* Invoke PTA_CMD_GET_DEVICES function */ - inv_arg.func = PTA_CMD_GET_DEVICES; + inv_arg.func = func; inv_arg.session = session; inv_arg.num_params = 4; @@ -65,7 +53,7 @@ static int get_devices(struct tee_context *ctx, u32 session, return 0; } -static int optee_register_device(const uuid_t *device_uuid, u32 device_id) +static int optee_register_device(const uuid_t *device_uuid) { struct tee_client_device *optee_device = NULL; int rc; @@ -75,7 +63,10 @@ static int optee_register_device(const uuid_t *device_uuid, u32 device_id) return -ENOMEM; optee_device->dev.bus = &tee_bus_type; - dev_set_name(&optee_device->dev, "optee-clnt%u", device_id); + if (dev_set_name(&optee_device->dev, "optee-ta-%pUb", device_uuid)) { + kfree(optee_device); + return -ENOMEM; + } uuid_copy(&optee_device->id.uuid, device_uuid); rc = device_register(&optee_device->dev); @@ -87,7 +78,7 @@ static int optee_register_device(const uuid_t *device_uuid, u32 device_id) return rc; } -int optee_enumerate_devices(void) +static int __optee_enumerate_devices(u32 func) { const uuid_t pta_uuid = UUID_INIT(0x7011a688, 0xddde, 0x4053, @@ -118,7 +109,7 @@ int optee_enumerate_devices(void) goto out_ctx; } - rc = get_devices(ctx, sess_arg.session, NULL, &shm_size); + rc = get_devices(ctx, sess_arg.session, NULL, &shm_size, func); if (rc < 0 || !shm_size) goto out_sess; @@ -130,7 +121,7 @@ int optee_enumerate_devices(void) goto out_sess; } - rc = get_devices(ctx, sess_arg.session, device_shm, &shm_size); + rc = get_devices(ctx, sess_arg.session, device_shm, &shm_size, func); if (rc < 0) goto out_shm; @@ -144,7 +135,7 @@ int optee_enumerate_devices(void) num_devices = shm_size / sizeof(uuid_t); for (idx = 0; idx < num_devices; idx++) { - rc = optee_register_device(&device_uuid[idx], idx); + rc = optee_register_device(&device_uuid[idx]); if (rc) goto out_shm; } @@ -158,3 +149,8 @@ out_ctx: return rc; } + +int optee_enumerate_devices(u32 func) +{ + return __optee_enumerate_devices(func); +} diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index d9c5037b4e03..8b71839a357e 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -78,6 +78,9 @@ struct optee_supp { * @memremaped_shm virtual address of memory in shared memory pool * @sec_caps: secure world capabilities defined by * OPTEE_SMC_SEC_CAP_* in optee_smc.h + * @scan_bus_done flag if device registation was already done. + * @scan_bus_wq workqueue to scan optee bus and register optee drivers + * @scan_bus_work workq to scan optee bus and register optee drivers */ struct optee { struct tee_device *supp_teedev; @@ -89,6 +92,9 @@ struct optee { struct tee_shm_pool *pool; void *memremaped_shm; u32 sec_caps; + bool scan_bus_done; + struct workqueue_struct *scan_bus_wq; + struct work_struct scan_bus_work; }; struct optee_session { @@ -173,7 +179,9 @@ void optee_free_pages_list(void *array, size_t num_entries); void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, size_t page_offset); -int optee_enumerate_devices(void); +#define PTA_CMD_GET_DEVICES 0x0 +#define PTA_CMD_GET_DEVICES_SUPP 0x1 +int optee_enumerate_devices(u32 func); /* * Small helpers diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index e8ab583e5098..113116d3585c 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -107,7 +107,7 @@ static void tosa_lcd_tg_on(struct tosa_lcd_data *data) /* TG LCD GVSS */ tosa_tg_send(spi, TG_PINICTL, 0x0); - if (!data->i2c) { + if (IS_ERR_OR_NULL(data->i2c)) { /* * after the pannel is powered up the first time, * we can access the i2c bus so probe for the DAC @@ -119,7 +119,7 @@ static void tosa_lcd_tg_on(struct tosa_lcd_data *data) .addr = DAC_BASE, .platform_data = data->spi, }; - data->i2c = i2c_new_device(adap, &info); + data->i2c = i2c_new_client_device(adap, &info); } } diff --git a/drivers/video/fbdev/hpfb.c b/drivers/video/fbdev/hpfb.c index f02be0db335e..8d418abdd767 100644 --- a/drivers/video/fbdev/hpfb.c +++ b/drivers/video/fbdev/hpfb.c @@ -402,7 +402,7 @@ int __init hpfb_init(void) if (err) return err; - err = probe_kernel_read(&i, (unsigned char *)INTFBVADDR + DIO_IDOFF, 1); + err = copy_from_kernel_nofault(&i, (unsigned char *)INTFBVADDR + DIO_IDOFF, 1); if (!err && (i == DIO_ID_FBUFFER) && topcat_sid_ok(sid = DIO_SECID(INTFBVADDR))) { if (!request_mem_region(INTFBPADDR, DIO_DEVSIZE, "Internal Topcat")) diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h index 3041092e84b3..449680a61569 100644 --- a/drivers/w1/w1_netlink.h +++ b/drivers/w1/w1_netlink.h @@ -73,7 +73,7 @@ struct w1_netlink_msg __u32 res; } mst; } id; - __u8 data[0]; + __u8 data[]; }; /** @@ -122,7 +122,7 @@ struct w1_netlink_cmd __u8 cmd; __u8 res; __u16 len; - __u8 data[0]; + __u8 data[]; }; #ifdef __KERNEL__ |