162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DFL device driver for EMIF private feature 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 Intel Corporation, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/bitfield.h> 962306a36Sopenharmony_ci#include <linux/dfl.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/iopoll.h> 1362306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/spinlock.h> 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define FME_FEATURE_ID_EMIF 0x9 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define EMIF_STAT 0x8 2262306a36Sopenharmony_ci#define EMIF_STAT_INIT_DONE_SFT 0 2362306a36Sopenharmony_ci#define EMIF_STAT_CALC_FAIL_SFT 8 2462306a36Sopenharmony_ci#define EMIF_STAT_CLEAR_BUSY_SFT 16 2562306a36Sopenharmony_ci#define EMIF_CTRL 0x10 2662306a36Sopenharmony_ci#define EMIF_CTRL_CLEAR_EN_SFT 0 2762306a36Sopenharmony_ci#define EMIF_CTRL_CLEAR_EN_MSK GENMASK_ULL(7, 0) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define EMIF_POLL_INVL 10000 /* us */ 3062306a36Sopenharmony_ci#define EMIF_POLL_TIMEOUT 5000000 /* us */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * The Capability Register replaces the Control Register (at the same 3462306a36Sopenharmony_ci * offset) for EMIF feature revisions > 0. The bitmask that indicates 3562306a36Sopenharmony_ci * the presence of memory channels exists in both the Capability Register 3662306a36Sopenharmony_ci * and Control Register definitions. These can be thought of as a C union. 3762306a36Sopenharmony_ci * The Capability Register definitions are used to check for the existence 3862306a36Sopenharmony_ci * of a memory channel, and the Control Register definitions are used for 3962306a36Sopenharmony_ci * managing the memory-clear functionality in revision 0. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define EMIF_CAPABILITY_BASE 0x10 4262306a36Sopenharmony_ci#define EMIF_CAPABILITY_CHN_MSK_V0 GENMASK_ULL(3, 0) 4362306a36Sopenharmony_ci#define EMIF_CAPABILITY_CHN_MSK GENMASK_ULL(7, 0) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct dfl_emif { 4662306a36Sopenharmony_ci struct device *dev; 4762306a36Sopenharmony_ci void __iomem *base; 4862306a36Sopenharmony_ci spinlock_t lock; /* Serialises access to EMIF_CTRL reg */ 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct emif_attr { 5262306a36Sopenharmony_ci struct device_attribute attr; 5362306a36Sopenharmony_ci u32 shift; 5462306a36Sopenharmony_ci u32 index; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define to_emif_attr(dev_attr) \ 5862306a36Sopenharmony_ci container_of(dev_attr, struct emif_attr, attr) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic ssize_t emif_state_show(struct device *dev, 6162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct emif_attr *eattr = to_emif_attr(attr); 6462306a36Sopenharmony_ci struct dfl_emif *de = dev_get_drvdata(dev); 6562306a36Sopenharmony_ci u64 val; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci val = readq(de->base + EMIF_STAT); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", 7062306a36Sopenharmony_ci !!(val & BIT_ULL(eattr->shift + eattr->index))); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic ssize_t emif_clear_store(struct device *dev, 7462306a36Sopenharmony_ci struct device_attribute *attr, 7562306a36Sopenharmony_ci const char *buf, size_t count) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct emif_attr *eattr = to_emif_attr(attr); 7862306a36Sopenharmony_ci struct dfl_emif *de = dev_get_drvdata(dev); 7962306a36Sopenharmony_ci u64 clear_busy_msk, clear_en_msk, val; 8062306a36Sopenharmony_ci void __iomem *base = de->base; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!sysfs_streq(buf, "1")) 8362306a36Sopenharmony_ci return -EINVAL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci clear_busy_msk = BIT_ULL(EMIF_STAT_CLEAR_BUSY_SFT + eattr->index); 8662306a36Sopenharmony_ci clear_en_msk = BIT_ULL(EMIF_CTRL_CLEAR_EN_SFT + eattr->index); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci spin_lock(&de->lock); 8962306a36Sopenharmony_ci /* The CLEAR_EN field is WO, but other fields are RW */ 9062306a36Sopenharmony_ci val = readq(base + EMIF_CTRL); 9162306a36Sopenharmony_ci val &= ~EMIF_CTRL_CLEAR_EN_MSK; 9262306a36Sopenharmony_ci val |= clear_en_msk; 9362306a36Sopenharmony_ci writeq(val, base + EMIF_CTRL); 9462306a36Sopenharmony_ci spin_unlock(&de->lock); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (readq_poll_timeout(base + EMIF_STAT, val, 9762306a36Sopenharmony_ci !(val & clear_busy_msk), 9862306a36Sopenharmony_ci EMIF_POLL_INVL, EMIF_POLL_TIMEOUT)) { 9962306a36Sopenharmony_ci dev_err(de->dev, "timeout, fail to clear\n"); 10062306a36Sopenharmony_ci return -ETIMEDOUT; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return count; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define emif_state_attr(_name, _shift, _index) \ 10762306a36Sopenharmony_ci static struct emif_attr emif_attr_##inf##_index##_##_name = \ 10862306a36Sopenharmony_ci { .attr = __ATTR(inf##_index##_##_name, 0444, \ 10962306a36Sopenharmony_ci emif_state_show, NULL), \ 11062306a36Sopenharmony_ci .shift = (_shift), .index = (_index) } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define emif_clear_attr(_index) \ 11362306a36Sopenharmony_ci static struct emif_attr emif_attr_##inf##_index##_clear = \ 11462306a36Sopenharmony_ci { .attr = __ATTR(inf##_index##_clear, 0200, \ 11562306a36Sopenharmony_ci NULL, emif_clear_store), \ 11662306a36Sopenharmony_ci .index = (_index) } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciemif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 0); 11962306a36Sopenharmony_ciemif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 1); 12062306a36Sopenharmony_ciemif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 2); 12162306a36Sopenharmony_ciemif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 3); 12262306a36Sopenharmony_ciemif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 4); 12362306a36Sopenharmony_ciemif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 5); 12462306a36Sopenharmony_ciemif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 6); 12562306a36Sopenharmony_ciemif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 7); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciemif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 0); 12862306a36Sopenharmony_ciemif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 1); 12962306a36Sopenharmony_ciemif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 2); 13062306a36Sopenharmony_ciemif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 3); 13162306a36Sopenharmony_ciemif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 4); 13262306a36Sopenharmony_ciemif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 5); 13362306a36Sopenharmony_ciemif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 6); 13462306a36Sopenharmony_ciemif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 7); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciemif_clear_attr(0); 13862306a36Sopenharmony_ciemif_clear_attr(1); 13962306a36Sopenharmony_ciemif_clear_attr(2); 14062306a36Sopenharmony_ciemif_clear_attr(3); 14162306a36Sopenharmony_ciemif_clear_attr(4); 14262306a36Sopenharmony_ciemif_clear_attr(5); 14362306a36Sopenharmony_ciemif_clear_attr(6); 14462306a36Sopenharmony_ciemif_clear_attr(7); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic struct attribute *dfl_emif_attrs[] = { 14862306a36Sopenharmony_ci &emif_attr_inf0_init_done.attr.attr, 14962306a36Sopenharmony_ci &emif_attr_inf0_cal_fail.attr.attr, 15062306a36Sopenharmony_ci &emif_attr_inf0_clear.attr.attr, 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci &emif_attr_inf1_init_done.attr.attr, 15362306a36Sopenharmony_ci &emif_attr_inf1_cal_fail.attr.attr, 15462306a36Sopenharmony_ci &emif_attr_inf1_clear.attr.attr, 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci &emif_attr_inf2_init_done.attr.attr, 15762306a36Sopenharmony_ci &emif_attr_inf2_cal_fail.attr.attr, 15862306a36Sopenharmony_ci &emif_attr_inf2_clear.attr.attr, 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci &emif_attr_inf3_init_done.attr.attr, 16162306a36Sopenharmony_ci &emif_attr_inf3_cal_fail.attr.attr, 16262306a36Sopenharmony_ci &emif_attr_inf3_clear.attr.attr, 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci &emif_attr_inf4_init_done.attr.attr, 16562306a36Sopenharmony_ci &emif_attr_inf4_cal_fail.attr.attr, 16662306a36Sopenharmony_ci &emif_attr_inf4_clear.attr.attr, 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci &emif_attr_inf5_init_done.attr.attr, 16962306a36Sopenharmony_ci &emif_attr_inf5_cal_fail.attr.attr, 17062306a36Sopenharmony_ci &emif_attr_inf5_clear.attr.attr, 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci &emif_attr_inf6_init_done.attr.attr, 17362306a36Sopenharmony_ci &emif_attr_inf6_cal_fail.attr.attr, 17462306a36Sopenharmony_ci &emif_attr_inf6_clear.attr.attr, 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci &emif_attr_inf7_init_done.attr.attr, 17762306a36Sopenharmony_ci &emif_attr_inf7_cal_fail.attr.attr, 17862306a36Sopenharmony_ci &emif_attr_inf7_clear.attr.attr, 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci NULL, 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic umode_t dfl_emif_visible(struct kobject *kobj, 18462306a36Sopenharmony_ci struct attribute *attr, int n) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct dfl_emif *de = dev_get_drvdata(kobj_to_dev(kobj)); 18762306a36Sopenharmony_ci struct emif_attr *eattr = container_of(attr, struct emif_attr, 18862306a36Sopenharmony_ci attr.attr); 18962306a36Sopenharmony_ci struct dfl_device *ddev = to_dfl_dev(de->dev); 19062306a36Sopenharmony_ci u64 val; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * This device supports up to 8 memory interfaces, but not all 19462306a36Sopenharmony_ci * interfaces are used on different platforms. The read out value of 19562306a36Sopenharmony_ci * CAPABILITY_CHN_MSK field (which is a bitmap) indicates which 19662306a36Sopenharmony_ci * interfaces are available. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci if (ddev->revision > 0 && strstr(attr->name, "_clear")) 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (ddev->revision == 0) 20262306a36Sopenharmony_ci val = FIELD_GET(EMIF_CAPABILITY_CHN_MSK_V0, 20362306a36Sopenharmony_ci readq(de->base + EMIF_CAPABILITY_BASE)); 20462306a36Sopenharmony_ci else 20562306a36Sopenharmony_ci val = FIELD_GET(EMIF_CAPABILITY_CHN_MSK, 20662306a36Sopenharmony_ci readq(de->base + EMIF_CAPABILITY_BASE)); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return (val & BIT_ULL(eattr->index)) ? attr->mode : 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic const struct attribute_group dfl_emif_group = { 21262306a36Sopenharmony_ci .is_visible = dfl_emif_visible, 21362306a36Sopenharmony_ci .attrs = dfl_emif_attrs, 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic const struct attribute_group *dfl_emif_groups[] = { 21762306a36Sopenharmony_ci &dfl_emif_group, 21862306a36Sopenharmony_ci NULL, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int dfl_emif_probe(struct dfl_device *ddev) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct device *dev = &ddev->dev; 22462306a36Sopenharmony_ci struct dfl_emif *de; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci de = devm_kzalloc(dev, sizeof(*de), GFP_KERNEL); 22762306a36Sopenharmony_ci if (!de) 22862306a36Sopenharmony_ci return -ENOMEM; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci de->base = devm_ioremap_resource(dev, &ddev->mmio_res); 23162306a36Sopenharmony_ci if (IS_ERR(de->base)) 23262306a36Sopenharmony_ci return PTR_ERR(de->base); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci de->dev = dev; 23562306a36Sopenharmony_ci spin_lock_init(&de->lock); 23662306a36Sopenharmony_ci dev_set_drvdata(dev, de); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic const struct dfl_device_id dfl_emif_ids[] = { 24262306a36Sopenharmony_ci { FME_ID, FME_FEATURE_ID_EMIF }, 24362306a36Sopenharmony_ci { } 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(dfl, dfl_emif_ids); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic struct dfl_driver dfl_emif_driver = { 24862306a36Sopenharmony_ci .drv = { 24962306a36Sopenharmony_ci .name = "dfl-emif", 25062306a36Sopenharmony_ci .dev_groups = dfl_emif_groups, 25162306a36Sopenharmony_ci }, 25262306a36Sopenharmony_ci .id_table = dfl_emif_ids, 25362306a36Sopenharmony_ci .probe = dfl_emif_probe, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_cimodule_dfl_driver(dfl_emif_driver); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciMODULE_DESCRIPTION("DFL EMIF driver"); 25862306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 25962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 260