162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/device.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/iopoll.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1362306a36Sopenharmony_ci#include <linux/nvmem-provider.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/pm_domain.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/property.h> 1862306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Blow timer clock frequency in Mhz */ 2162306a36Sopenharmony_ci#define QFPROM_BLOW_TIMER_OFFSET 0x03c 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Amount of time required to hold charge to blow fuse in micro-seconds */ 2462306a36Sopenharmony_ci#define QFPROM_FUSE_BLOW_POLL_US 100 2562306a36Sopenharmony_ci#define QFPROM_FUSE_BLOW_TIMEOUT_US 10000 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define QFPROM_BLOW_STATUS_OFFSET 0x048 2862306a36Sopenharmony_ci#define QFPROM_BLOW_STATUS_BUSY 0x1 2962306a36Sopenharmony_ci#define QFPROM_BLOW_STATUS_READY 0x0 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define QFPROM_ACCEL_OFFSET 0x044 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define QFPROM_VERSION_OFFSET 0x0 3462306a36Sopenharmony_ci#define QFPROM_MAJOR_VERSION_SHIFT 28 3562306a36Sopenharmony_ci#define QFPROM_MAJOR_VERSION_MASK GENMASK(31, QFPROM_MAJOR_VERSION_SHIFT) 3662306a36Sopenharmony_ci#define QFPROM_MINOR_VERSION_SHIFT 16 3762306a36Sopenharmony_ci#define QFPROM_MINOR_VERSION_MASK GENMASK(27, QFPROM_MINOR_VERSION_SHIFT) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic bool read_raw_data; 4062306a36Sopenharmony_cimodule_param(read_raw_data, bool, 0644); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(read_raw_data, "Read raw instead of corrected data"); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * struct qfprom_soc_data - config that varies from SoC to SoC. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * @accel_value: Should contain qfprom accel value. 4762306a36Sopenharmony_ci * @qfprom_blow_timer_value: The timer value of qfprom when doing efuse blow. 4862306a36Sopenharmony_ci * @qfprom_blow_set_freq: The frequency required to set when we start the 4962306a36Sopenharmony_ci * fuse blowing. 5062306a36Sopenharmony_ci * @qfprom_blow_uV: LDO voltage to be set when doing efuse blow 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistruct qfprom_soc_data { 5362306a36Sopenharmony_ci u32 accel_value; 5462306a36Sopenharmony_ci u32 qfprom_blow_timer_value; 5562306a36Sopenharmony_ci u32 qfprom_blow_set_freq; 5662306a36Sopenharmony_ci int qfprom_blow_uV; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/** 6062306a36Sopenharmony_ci * struct qfprom_priv - structure holding qfprom attributes 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * @qfpraw: iomapped memory space for qfprom-efuse raw address space. 6362306a36Sopenharmony_ci * @qfpconf: iomapped memory space for qfprom-efuse configuration address 6462306a36Sopenharmony_ci * space. 6562306a36Sopenharmony_ci * @qfpcorrected: iomapped memory space for qfprom corrected address space. 6662306a36Sopenharmony_ci * @qfpsecurity: iomapped memory space for qfprom security control space. 6762306a36Sopenharmony_ci * @dev: qfprom device structure. 6862306a36Sopenharmony_ci * @secclk: Clock supply. 6962306a36Sopenharmony_ci * @vcc: Regulator supply. 7062306a36Sopenharmony_ci * @soc_data: Data that for things that varies from SoC to SoC. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistruct qfprom_priv { 7362306a36Sopenharmony_ci void __iomem *qfpraw; 7462306a36Sopenharmony_ci void __iomem *qfpconf; 7562306a36Sopenharmony_ci void __iomem *qfpcorrected; 7662306a36Sopenharmony_ci void __iomem *qfpsecurity; 7762306a36Sopenharmony_ci struct device *dev; 7862306a36Sopenharmony_ci struct clk *secclk; 7962306a36Sopenharmony_ci struct regulator *vcc; 8062306a36Sopenharmony_ci const struct qfprom_soc_data *soc_data; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * struct qfprom_touched_values - saved values to restore after blowing 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * @clk_rate: The rate the clock was at before blowing. 8762306a36Sopenharmony_ci * @accel_val: The value of the accel reg before blowing. 8862306a36Sopenharmony_ci * @timer_val: The value of the timer before blowing. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistruct qfprom_touched_values { 9162306a36Sopenharmony_ci unsigned long clk_rate; 9262306a36Sopenharmony_ci u32 accel_val; 9362306a36Sopenharmony_ci u32 timer_val; 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * struct qfprom_soc_compatible_data - Data matched against the SoC 9862306a36Sopenharmony_ci * compatible string. 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * @keepout: Array of keepout regions for this SoC. 10162306a36Sopenharmony_ci * @nkeepout: Number of elements in the keepout array. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistruct qfprom_soc_compatible_data { 10462306a36Sopenharmony_ci const struct nvmem_keepout *keepout; 10562306a36Sopenharmony_ci unsigned int nkeepout; 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic const struct nvmem_keepout sc7180_qfprom_keepout[] = { 10962306a36Sopenharmony_ci {.start = 0x128, .end = 0x148}, 11062306a36Sopenharmony_ci {.start = 0x220, .end = 0x228} 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const struct qfprom_soc_compatible_data sc7180_qfprom = { 11462306a36Sopenharmony_ci .keepout = sc7180_qfprom_keepout, 11562306a36Sopenharmony_ci .nkeepout = ARRAY_SIZE(sc7180_qfprom_keepout) 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const struct nvmem_keepout sc7280_qfprom_keepout[] = { 11962306a36Sopenharmony_ci {.start = 0x128, .end = 0x148}, 12062306a36Sopenharmony_ci {.start = 0x238, .end = 0x248} 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const struct qfprom_soc_compatible_data sc7280_qfprom = { 12462306a36Sopenharmony_ci .keepout = sc7280_qfprom_keepout, 12562306a36Sopenharmony_ci .nkeepout = ARRAY_SIZE(sc7280_qfprom_keepout) 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * qfprom_disable_fuse_blowing() - Undo enabling of fuse blowing. 13062306a36Sopenharmony_ci * @priv: Our driver data. 13162306a36Sopenharmony_ci * @old: The data that was stashed from before fuse blowing. 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Resets the value of the blow timer, accel register and the clock 13462306a36Sopenharmony_ci * and voltage settings. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * Prints messages if there are errors but doesn't return an error code 13762306a36Sopenharmony_ci * since there's not much we can do upon failure. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistatic void qfprom_disable_fuse_blowing(const struct qfprom_priv *priv, 14062306a36Sopenharmony_ci const struct qfprom_touched_values *old) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci writel(old->timer_val, priv->qfpconf + QFPROM_BLOW_TIMER_OFFSET); 14562306a36Sopenharmony_ci writel(old->accel_val, priv->qfpconf + QFPROM_ACCEL_OFFSET); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dev_pm_genpd_set_performance_state(priv->dev, 0); 14862306a36Sopenharmony_ci pm_runtime_put(priv->dev); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * This may be a shared rail and may be able to run at a lower rate 15262306a36Sopenharmony_ci * when we're not blowing fuses. At the moment, the regulator framework 15362306a36Sopenharmony_ci * applies voltage constraints even on disabled rails, so remove our 15462306a36Sopenharmony_ci * constraints and allow the rail to be adjusted by other users. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci ret = regulator_set_voltage(priv->vcc, 0, INT_MAX); 15762306a36Sopenharmony_ci if (ret) 15862306a36Sopenharmony_ci dev_warn(priv->dev, "Failed to set 0 voltage (ignoring)\n"); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ret = regulator_disable(priv->vcc); 16162306a36Sopenharmony_ci if (ret) 16262306a36Sopenharmony_ci dev_warn(priv->dev, "Failed to disable regulator (ignoring)\n"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ret = clk_set_rate(priv->secclk, old->clk_rate); 16562306a36Sopenharmony_ci if (ret) 16662306a36Sopenharmony_ci dev_warn(priv->dev, 16762306a36Sopenharmony_ci "Failed to set clock rate for disable (ignoring)\n"); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci clk_disable_unprepare(priv->secclk); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/** 17362306a36Sopenharmony_ci * qfprom_enable_fuse_blowing() - Enable fuse blowing. 17462306a36Sopenharmony_ci * @priv: Our driver data. 17562306a36Sopenharmony_ci * @old: We'll stash stuff here to use when disabling. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * Sets the value of the blow timer, accel register and the clock 17862306a36Sopenharmony_ci * and voltage settings. 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * Prints messages if there are errors so caller doesn't need to. 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * Return: 0 or -err. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic int qfprom_enable_fuse_blowing(const struct qfprom_priv *priv, 18562306a36Sopenharmony_ci struct qfprom_touched_values *old) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int ret; 18862306a36Sopenharmony_ci int qfprom_blow_uV = priv->soc_data->qfprom_blow_uV; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ret = clk_prepare_enable(priv->secclk); 19162306a36Sopenharmony_ci if (ret) { 19262306a36Sopenharmony_ci dev_err(priv->dev, "Failed to enable clock\n"); 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci old->clk_rate = clk_get_rate(priv->secclk); 19762306a36Sopenharmony_ci ret = clk_set_rate(priv->secclk, priv->soc_data->qfprom_blow_set_freq); 19862306a36Sopenharmony_ci if (ret) { 19962306a36Sopenharmony_ci dev_err(priv->dev, "Failed to set clock rate for enable\n"); 20062306a36Sopenharmony_ci goto err_clk_prepared; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * Hardware requires a minimum voltage for fuse blowing. 20562306a36Sopenharmony_ci * This may be a shared rail so don't specify a maximum. 20662306a36Sopenharmony_ci * Regulator constraints will cap to the actual maximum. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci ret = regulator_set_voltage(priv->vcc, qfprom_blow_uV, INT_MAX); 20962306a36Sopenharmony_ci if (ret) { 21062306a36Sopenharmony_ci dev_err(priv->dev, "Failed to set %duV\n", qfprom_blow_uV); 21162306a36Sopenharmony_ci goto err_clk_rate_set; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci ret = regulator_enable(priv->vcc); 21562306a36Sopenharmony_ci if (ret) { 21662306a36Sopenharmony_ci dev_err(priv->dev, "Failed to enable regulator\n"); 21762306a36Sopenharmony_ci goto err_clk_rate_set; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(priv->dev); 22162306a36Sopenharmony_ci if (ret < 0) { 22262306a36Sopenharmony_ci dev_err(priv->dev, "Failed to enable power-domain\n"); 22362306a36Sopenharmony_ci goto err_reg_enable; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci dev_pm_genpd_set_performance_state(priv->dev, INT_MAX); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci old->timer_val = readl(priv->qfpconf + QFPROM_BLOW_TIMER_OFFSET); 22862306a36Sopenharmony_ci old->accel_val = readl(priv->qfpconf + QFPROM_ACCEL_OFFSET); 22962306a36Sopenharmony_ci writel(priv->soc_data->qfprom_blow_timer_value, 23062306a36Sopenharmony_ci priv->qfpconf + QFPROM_BLOW_TIMER_OFFSET); 23162306a36Sopenharmony_ci writel(priv->soc_data->accel_value, 23262306a36Sopenharmony_ci priv->qfpconf + QFPROM_ACCEL_OFFSET); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cierr_reg_enable: 23762306a36Sopenharmony_ci regulator_disable(priv->vcc); 23862306a36Sopenharmony_cierr_clk_rate_set: 23962306a36Sopenharmony_ci clk_set_rate(priv->secclk, old->clk_rate); 24062306a36Sopenharmony_cierr_clk_prepared: 24162306a36Sopenharmony_ci clk_disable_unprepare(priv->secclk); 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * qfprom_reg_write() - Write to fuses. 24762306a36Sopenharmony_ci * @context: Our driver data. 24862306a36Sopenharmony_ci * @reg: The offset to write at. 24962306a36Sopenharmony_ci * @_val: Pointer to data to write. 25062306a36Sopenharmony_ci * @bytes: The number of bytes to write. 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * Writes to fuses. WARNING: THIS IS PERMANENT. 25362306a36Sopenharmony_ci * 25462306a36Sopenharmony_ci * Return: 0 or -err. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistatic int qfprom_reg_write(void *context, unsigned int reg, void *_val, 25762306a36Sopenharmony_ci size_t bytes) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct qfprom_priv *priv = context; 26062306a36Sopenharmony_ci struct qfprom_touched_values old; 26162306a36Sopenharmony_ci int words = bytes / 4; 26262306a36Sopenharmony_ci u32 *value = _val; 26362306a36Sopenharmony_ci u32 blow_status; 26462306a36Sopenharmony_ci int ret; 26562306a36Sopenharmony_ci int i; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci dev_dbg(priv->dev, 26862306a36Sopenharmony_ci "Writing to raw qfprom region : %#010x of size: %zu\n", 26962306a36Sopenharmony_ci reg, bytes); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * The hardware only allows us to write word at a time, but we can 27362306a36Sopenharmony_ci * read byte at a time. Until the nvmem framework allows a separate 27462306a36Sopenharmony_ci * word_size and stride for reading vs. writing, we'll enforce here. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci if (bytes % 4) { 27762306a36Sopenharmony_ci dev_err(priv->dev, 27862306a36Sopenharmony_ci "%zu is not an integral number of words\n", bytes); 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci if (reg % 4) { 28262306a36Sopenharmony_ci dev_err(priv->dev, 28362306a36Sopenharmony_ci "Invalid offset: %#x. Must be word aligned\n", reg); 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = qfprom_enable_fuse_blowing(priv, &old); 28862306a36Sopenharmony_ci if (ret) 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout( 29262306a36Sopenharmony_ci priv->qfpconf + QFPROM_BLOW_STATUS_OFFSET, 29362306a36Sopenharmony_ci blow_status, blow_status == QFPROM_BLOW_STATUS_READY, 29462306a36Sopenharmony_ci QFPROM_FUSE_BLOW_POLL_US, QFPROM_FUSE_BLOW_TIMEOUT_US); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (ret) { 29762306a36Sopenharmony_ci dev_err(priv->dev, 29862306a36Sopenharmony_ci "Timeout waiting for initial ready; aborting.\n"); 29962306a36Sopenharmony_ci goto exit_enabled_fuse_blowing; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci for (i = 0; i < words; i++) 30362306a36Sopenharmony_ci writel(value[i], priv->qfpraw + reg + (i * 4)); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout( 30662306a36Sopenharmony_ci priv->qfpconf + QFPROM_BLOW_STATUS_OFFSET, 30762306a36Sopenharmony_ci blow_status, blow_status == QFPROM_BLOW_STATUS_READY, 30862306a36Sopenharmony_ci QFPROM_FUSE_BLOW_POLL_US, QFPROM_FUSE_BLOW_TIMEOUT_US); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Give an error, but not much we can do in this case */ 31162306a36Sopenharmony_ci if (ret) 31262306a36Sopenharmony_ci dev_err(priv->dev, "Timeout waiting for finish.\n"); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ciexit_enabled_fuse_blowing: 31562306a36Sopenharmony_ci qfprom_disable_fuse_blowing(priv, &old); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int qfprom_reg_read(void *context, 32162306a36Sopenharmony_ci unsigned int reg, void *_val, size_t bytes) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct qfprom_priv *priv = context; 32462306a36Sopenharmony_ci u8 *val = _val; 32562306a36Sopenharmony_ci int i = 0, words = bytes; 32662306a36Sopenharmony_ci void __iomem *base = priv->qfpcorrected; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (read_raw_data && priv->qfpraw) 32962306a36Sopenharmony_ci base = priv->qfpraw; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci while (words--) 33262306a36Sopenharmony_ci *val++ = readb(base + reg + i++); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void qfprom_runtime_disable(void *data) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci pm_runtime_disable(data); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic const struct qfprom_soc_data qfprom_7_8_data = { 34362306a36Sopenharmony_ci .accel_value = 0xD10, 34462306a36Sopenharmony_ci .qfprom_blow_timer_value = 25, 34562306a36Sopenharmony_ci .qfprom_blow_set_freq = 4800000, 34662306a36Sopenharmony_ci .qfprom_blow_uV = 1800000, 34762306a36Sopenharmony_ci}; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic const struct qfprom_soc_data qfprom_7_15_data = { 35062306a36Sopenharmony_ci .accel_value = 0xD08, 35162306a36Sopenharmony_ci .qfprom_blow_timer_value = 24, 35262306a36Sopenharmony_ci .qfprom_blow_set_freq = 4800000, 35362306a36Sopenharmony_ci .qfprom_blow_uV = 1900000, 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int qfprom_probe(struct platform_device *pdev) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct nvmem_config econfig = { 35962306a36Sopenharmony_ci .name = "qfprom", 36062306a36Sopenharmony_ci .stride = 1, 36162306a36Sopenharmony_ci .word_size = 1, 36262306a36Sopenharmony_ci .id = NVMEM_DEVID_AUTO, 36362306a36Sopenharmony_ci .reg_read = qfprom_reg_read, 36462306a36Sopenharmony_ci }; 36562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 36662306a36Sopenharmony_ci struct resource *res; 36762306a36Sopenharmony_ci struct nvmem_device *nvmem; 36862306a36Sopenharmony_ci const struct qfprom_soc_compatible_data *soc_data; 36962306a36Sopenharmony_ci struct qfprom_priv *priv; 37062306a36Sopenharmony_ci int ret; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 37362306a36Sopenharmony_ci if (!priv) 37462306a36Sopenharmony_ci return -ENOMEM; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* The corrected section is always provided */ 37762306a36Sopenharmony_ci priv->qfpcorrected = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 37862306a36Sopenharmony_ci if (IS_ERR(priv->qfpcorrected)) 37962306a36Sopenharmony_ci return PTR_ERR(priv->qfpcorrected); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci econfig.size = resource_size(res); 38262306a36Sopenharmony_ci econfig.dev = dev; 38362306a36Sopenharmony_ci econfig.priv = priv; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci priv->dev = dev; 38662306a36Sopenharmony_ci soc_data = device_get_match_data(dev); 38762306a36Sopenharmony_ci if (soc_data) { 38862306a36Sopenharmony_ci econfig.keepout = soc_data->keepout; 38962306a36Sopenharmony_ci econfig.nkeepout = soc_data->nkeepout; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * If more than one region is provided then the OS has the ability 39462306a36Sopenharmony_ci * to write. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 39762306a36Sopenharmony_ci if (res) { 39862306a36Sopenharmony_ci u32 version; 39962306a36Sopenharmony_ci int major_version, minor_version; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci priv->qfpraw = devm_ioremap_resource(dev, res); 40262306a36Sopenharmony_ci if (IS_ERR(priv->qfpraw)) 40362306a36Sopenharmony_ci return PTR_ERR(priv->qfpraw); 40462306a36Sopenharmony_ci priv->qfpconf = devm_platform_ioremap_resource(pdev, 2); 40562306a36Sopenharmony_ci if (IS_ERR(priv->qfpconf)) 40662306a36Sopenharmony_ci return PTR_ERR(priv->qfpconf); 40762306a36Sopenharmony_ci priv->qfpsecurity = devm_platform_ioremap_resource(pdev, 3); 40862306a36Sopenharmony_ci if (IS_ERR(priv->qfpsecurity)) 40962306a36Sopenharmony_ci return PTR_ERR(priv->qfpsecurity); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci version = readl(priv->qfpsecurity + QFPROM_VERSION_OFFSET); 41262306a36Sopenharmony_ci major_version = (version & QFPROM_MAJOR_VERSION_MASK) >> 41362306a36Sopenharmony_ci QFPROM_MAJOR_VERSION_SHIFT; 41462306a36Sopenharmony_ci minor_version = (version & QFPROM_MINOR_VERSION_MASK) >> 41562306a36Sopenharmony_ci QFPROM_MINOR_VERSION_SHIFT; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (major_version == 7 && minor_version == 8) 41862306a36Sopenharmony_ci priv->soc_data = &qfprom_7_8_data; 41962306a36Sopenharmony_ci else if (major_version == 7 && minor_version == 15) 42062306a36Sopenharmony_ci priv->soc_data = &qfprom_7_15_data; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci priv->vcc = devm_regulator_get(&pdev->dev, "vcc"); 42362306a36Sopenharmony_ci if (IS_ERR(priv->vcc)) 42462306a36Sopenharmony_ci return PTR_ERR(priv->vcc); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci priv->secclk = devm_clk_get(dev, "core"); 42762306a36Sopenharmony_ci if (IS_ERR(priv->secclk)) 42862306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->secclk), "Error getting clock\n"); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Only enable writing if we have SoC data. */ 43162306a36Sopenharmony_ci if (priv->soc_data) 43262306a36Sopenharmony_ci econfig.reg_write = qfprom_reg_write; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci pm_runtime_enable(dev); 43662306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, qfprom_runtime_disable, dev); 43762306a36Sopenharmony_ci if (ret) 43862306a36Sopenharmony_ci return ret; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci nvmem = devm_nvmem_register(dev, &econfig); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(nvmem); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic const struct of_device_id qfprom_of_match[] = { 44662306a36Sopenharmony_ci { .compatible = "qcom,qfprom",}, 44762306a36Sopenharmony_ci { .compatible = "qcom,sc7180-qfprom", .data = &sc7180_qfprom}, 44862306a36Sopenharmony_ci { .compatible = "qcom,sc7280-qfprom", .data = &sc7280_qfprom}, 44962306a36Sopenharmony_ci {/* sentinel */}, 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qfprom_of_match); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic struct platform_driver qfprom_driver = { 45462306a36Sopenharmony_ci .probe = qfprom_probe, 45562306a36Sopenharmony_ci .driver = { 45662306a36Sopenharmony_ci .name = "qcom,qfprom", 45762306a36Sopenharmony_ci .of_match_table = qfprom_of_match, 45862306a36Sopenharmony_ci }, 45962306a36Sopenharmony_ci}; 46062306a36Sopenharmony_cimodule_platform_driver(qfprom_driver); 46162306a36Sopenharmony_ciMODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>"); 46262306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm QFPROM driver"); 46362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 464