From 1528bbe6b5702d4f2ec4c7c3e43a7ff7a7c058cc Mon Sep 17 00:00:00 2001 From: Sai Chaitanya Kaveti Date: Tue, 14 Dec 2021 18:24:33 +0530 Subject: [PATCH 01/29] msm: mhi_dev: Keeping event req memory without clearing in close channel Client initiated close channel is happening and transfer completion callback is not received within the timeout period. When completion callback is received it is becoming invalid as the memory is cleared as part of close channel previously. Here, keeping event req memory without clearing in close channel API and marking the elements in flush event request buffer as stale. Change-Id: I1e15cf51f0819252f79493d8f806352a12801ef6 Signed-off-by: Sai Chaitanya Kaveti --- drivers/platform/msm/mhi_dev/mhi.c | 28 +++++++++++++--------------- drivers/platform/msm/mhi_dev/mhi.h | 1 + 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index a6e5f1c52797..42a61d452a4d 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -372,8 +372,8 @@ static void mhi_dev_event_msi_cb(void *req) ch->pend_flush_cnt = 0; mhi = ch->ring->mhi_dev; - mhi_log(MHI_MSG_VERBOSE, "MSI completed for flush req %d\n", - ereq->flush_num); + mhi_log(MHI_MSG_VERBOSE, "MSI completed for %s flush req %d\n", + ereq->is_stale ? "stale" : "", ereq->flush_num); /* Add back the flushed events space to the event buffer */ ch->evt_buf_wp = ereq->start + ereq->num_events; @@ -383,8 +383,11 @@ static void mhi_dev_event_msi_cb(void *req) spin_lock_irqsave(&mhi->lock, flags); if (ch->curr_ereq == NULL) ch->curr_ereq = ereq; - else + else { + if (ereq->is_stale) + ereq->is_stale = false; list_add_tail(&ereq->list, &ch->event_req_buffers); + } spin_unlock_irqrestore(&mhi->lock, flags); } @@ -3256,6 +3259,7 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) struct mhi_dev_channel *ch; int count = 0; int rc = 0; + struct event_req *itr, *tmp; if (!handle) { mhi_log(MHI_MSG_ERROR, "Invalid channel access:%d\n", -ENODEV); return; @@ -3291,20 +3295,14 @@ void mhi_dev_close_channel(struct mhi_dev_client *handle) "Trying to close an active channel (%d)\n", ch->ch_id); + if (!list_empty(&ch->flush_event_req_buffers)) { + list_for_each_entry_safe(itr, tmp, &ch->flush_event_req_buffers, list) { + itr->is_stale = true; + } + } + ch->state = MHI_DEV_CH_CLOSED; ch->active_client = NULL; - kfree(ch->ereqs); - mhi_log(MHI_MSG_INFO, - "MEM_DEALLOC:ch:%d size:%d EREQ\n", - ch->ch_id, ch->evt_req_size); - kfree(ch->tr_events); - mhi_log(MHI_MSG_INFO, - "MEM_DEALLOC:ch:%d size:%d TR_EVENTS\n", - ch->ch_id, ch->evt_buf_size); - ch->evt_buf_size = 0; - ch->evt_req_size = 0; - ch->ereqs = NULL; - ch->tr_events = NULL; kfree(handle); mhi_log(MHI_MSG_INFO, "MEM_ALLOC:ch:%d size:%d CLNT_HANDLE\n", diff --git a/drivers/platform/msm/mhi_dev/mhi.h b/drivers/platform/msm/mhi_dev/mhi.h index c5ce1410328b..a7a34668a04f 100644 --- a/drivers/platform/msm/mhi_dev/mhi.h +++ b/drivers/platform/msm/mhi_dev/mhi.h @@ -452,6 +452,7 @@ struct event_req { struct list_head list; u32 flush_num; bool is_cmd_cpl; + bool is_stale; }; struct mhi_cmd_cmpl_ctx { From c72e4578563794dde6fa7a0e9116ad958b3c41e5 Mon Sep 17 00:00:00 2001 From: Gangadhar Kavalastramath Date: Thu, 4 Nov 2021 13:44:51 +0530 Subject: [PATCH 02/29] cnss2: Wlan is going into recovery mode due to pcie link down Due to pcie link down, wlan tried to recover from the failure, it checked for the current context to see if some memory has to be allocated in atomic context, but seems it failed to take into account one condition. To maintain wlan driver code as OS independent, code changes are done to allocate memory always with GFP_ATOMIC flag inside cnss_schedule_recovery() function. This avoids adding gfp flags inside wlan driver code. Change-Id: I255d22a46288eccc2056d870b5f3ee7575ee71eb Signed-off-by: Gangadhar Kavalastramath --- drivers/net/wireless/cnss2/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index fd99b1dc2700..10c25a806f05 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1421,7 +1421,6 @@ void cnss_schedule_recovery(struct device *dev, { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); struct cnss_recovery_data *data; - int gfp = GFP_KERNEL; if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) cnss_bus_update_status(plat_priv, CNSS_FW_DOWN); @@ -1432,10 +1431,11 @@ void cnss_schedule_recovery(struct device *dev, return; } - if (in_interrupt() || irqs_disabled()) - gfp = GFP_ATOMIC; - - data = kzalloc(sizeof(*data), gfp); + /* Allocating memory always with GFP_ATOMIC flag inside + * cnss_schedule_recovery(). Because there is a chance for + * this api to be invoked in atomic context on some platforms. + */ + data = kzalloc(sizeof(*data), GFP_ATOMIC); if (!data) return; From ec242d217db573db8ed83742806b09ea7c8121e5 Mon Sep 17 00:00:00 2001 From: Pradeep P V K Date: Tue, 11 May 2021 10:55:40 +0530 Subject: [PATCH 03/29] ubi: Add scrub_all support to UBI The data on NAND parts degrades overtime. This is known as data decay. This decay is accelarated by extreme temperatures. When data on nand is read over and over again this can also cause decay of data. This is known as read disturb and can degrade the data in the page/block that is read and the adjacent cells as well. This data degrade can be corrected to a certain degree by the nand driver/controller using the ECC but this is not sufficient specially in products designed to last years. The only way to combat the decay is to "refresh" the data by moving it to a new location. Add scrub_all support to UBI to facilitate the data refresh. Change-Id: Ifafb82fe4ddb7120277dcfbbff79b3e087ca344d Signed-off-by: Nikhilesh Reddy Signed-off-by: Pradeep P V K --- drivers/mtd/ubi/attach.c | 17 ++- drivers/mtd/ubi/block.c | 9 +- drivers/mtd/ubi/build.c | 111 +++++++++++++--- drivers/mtd/ubi/io.c | 11 ++ drivers/mtd/ubi/ubi.h | 14 +- drivers/mtd/ubi/vmt.c | 31 +++-- drivers/mtd/ubi/wl.c | 267 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 422 insertions(+), 38 deletions(-) diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index 10b2459f8951..d1f036393798 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -821,9 +821,22 @@ struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi, int err = 0; struct ubi_ainf_peb *aeb, *tmp_aeb; - if (!list_empty(&ai->free)) { - aeb = list_entry(ai->free.next, struct ubi_ainf_peb, u.list); + list_for_each_entry_safe(aeb, tmp_aeb, &ai->free, u.list) { + list_del(&aeb->u.list); + if (aeb->ec == UBI_UNKNOWN) { + ubi_err(ubi, "PEB %d in freelist has unknown EC", + aeb->pnum); + aeb->ec = ai->mean_ec; + } + err = early_erase_peb(ubi, ai, aeb->pnum, aeb->ec+1); + if (err) { + ubi_err(ubi, "Erase failed for PEB %d in freelist", + aeb->pnum); + list_add(&aeb->u.list, &ai->erase); + continue; + } + aeb->ec += 1; dbg_bld("return free PEB %d, EC %d", aeb->pnum, aeb->ec); return aeb; } diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c index e1a2ae21dfd3..ec476a5d0e37 100644 --- a/drivers/mtd/ubi/block.c +++ b/drivers/mtd/ubi/block.c @@ -121,7 +121,7 @@ static int __init ubiblock_set_param(const char *val, return -EINVAL; } - strcpy(buf, val); + strlcpy(buf, val, sizeof(buf)); /* Get rid of the final newline */ if (buf[len - 1] == '\n') @@ -141,12 +141,12 @@ static int __init ubiblock_set_param(const char *val, ret = kstrtoint(tokens[1], 10, ¶m->vol_id); if (ret < 0) { param->vol_id = -1; - strcpy(param->name, tokens[1]); + strlcpy(param->name, tokens[1], UBIBLOCK_PARAM_LEN+1); } } else { /* One parameter: must be device path */ - strcpy(param->name, tokens[0]); + strlcpy(param->name, tokens[0], UBIBLOCK_PARAM_LEN+1); param->ubi_num = -1; param->vol_id = -1; } @@ -412,7 +412,8 @@ int ubiblock_create(struct ubi_volume_info *vi) goto out_put_disk; } gd->private_data = dev; - sprintf(gd->disk_name, "ubiblock%d_%d", dev->ubi_num, dev->vol_id); + scnprintf(gd->disk_name, DISK_NAME_LEN, "ubiblock%d_%d", + dev->ubi_num, dev->vol_id); set_capacity(gd, disk_capacity); dev->gd = gd; diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index d636bbe214cb..971c3b7a51c3 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -95,7 +95,7 @@ static DEFINE_SPINLOCK(ubi_devices_lock); static ssize_t version_show(struct class *class, struct class_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", UBI_VERSION); + return scnprintf(buf, sizeof(int), "%d\n", UBI_VERSION); } static CLASS_ATTR_RO(version); @@ -114,6 +114,9 @@ struct class ubi_class = { static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t dev_attribute_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); /* UBI device attributes (correspond to files in '//class/ubi/ubiX') */ static struct device_attribute dev_eraseblock_size = @@ -140,6 +143,13 @@ static struct device_attribute dev_mtd_num = __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); static struct device_attribute dev_ro_mode = __ATTR(ro_mode, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_mtd_trigger_scrub = + __ATTR(scrub_all, 0644, + dev_attribute_show, dev_attribute_store); +static struct device_attribute dev_mtd_max_scrub_sqnum = + __ATTR(scrub_max_sqnum, 0444, dev_attribute_show, NULL); +static struct device_attribute dev_mtd_min_scrub_sqnum = + __ATTR(scrub_min_sqnum, 0444, dev_attribute_show, NULL); /** * ubi_volume_notify - send a volume change notification. @@ -332,6 +342,17 @@ int ubi_major2num(int major) return ubi_num; } +static unsigned long long get_max_sqnum(struct ubi_device *ubi) +{ + unsigned long long max_sqnum; + + spin_lock(&ubi->ltree_lock); + max_sqnum = ubi->global_sqnum - 1; + spin_unlock(&ubi->ltree_lock); + + return max_sqnum; +} + /* "Show" method for files in '//class/ubi/ubiX/' */ static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -355,29 +376,50 @@ static ssize_t dev_attribute_show(struct device *dev, return -ENODEV; if (attr == &dev_eraseblock_size) - ret = sprintf(buf, "%d\n", ubi->leb_size); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->leb_size); else if (attr == &dev_avail_eraseblocks) - ret = sprintf(buf, "%d\n", ubi->avail_pebs); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->avail_pebs); else if (attr == &dev_total_eraseblocks) - ret = sprintf(buf, "%d\n", ubi->good_peb_count); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->good_peb_count); else if (attr == &dev_volumes_count) - ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->vol_count - UBI_INT_VOL_COUNT); else if (attr == &dev_max_ec) - ret = sprintf(buf, "%d\n", ubi->max_ec); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->max_ec); else if (attr == &dev_reserved_for_bad) - ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->beb_rsvd_pebs); else if (attr == &dev_bad_peb_count) - ret = sprintf(buf, "%d\n", ubi->bad_peb_count); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->bad_peb_count); else if (attr == &dev_max_vol_count) - ret = sprintf(buf, "%d\n", ubi->vtbl_slots); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->vtbl_slots); else if (attr == &dev_min_io_size) - ret = sprintf(buf, "%d\n", ubi->min_io_size); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->min_io_size); else if (attr == &dev_bgt_enabled) - ret = sprintf(buf, "%d\n", ubi->thread_enabled); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->thread_enabled); else if (attr == &dev_mtd_num) - ret = sprintf(buf, "%d\n", ubi->mtd->index); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->mtd->index); else if (attr == &dev_ro_mode) - ret = sprintf(buf, "%d\n", ubi->ro_mode); + ret = scnprintf(buf, sizeof(int), "%d\n", + ubi->ro_mode); + else if (attr == &dev_mtd_trigger_scrub) + ret = scnprintf(buf, sizeof(int), "%d\n", + atomic_read(&ubi->scrub_work_count)); + else if (attr == &dev_mtd_max_scrub_sqnum) + ret = scnprintf(buf, sizeof(unsigned long long), "%llu\n", + get_max_sqnum(ubi)); + else if (attr == &dev_mtd_min_scrub_sqnum) + ret = scnprintf(buf, sizeof(unsigned long long), "%llu\n", + ubi_wl_scrub_get_min_sqnum(ubi)); else ret = -EINVAL; @@ -398,10 +440,46 @@ static struct attribute *ubi_dev_attrs[] = { &dev_bgt_enabled.attr, &dev_mtd_num.attr, &dev_ro_mode.attr, + &dev_mtd_trigger_scrub.attr, + &dev_mtd_max_scrub_sqnum.attr, + &dev_mtd_min_scrub_sqnum.attr, NULL }; ATTRIBUTE_GROUPS(ubi_dev); +static ssize_t dev_attribute_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + struct ubi_device *ubi; + unsigned long long scrub_sqnum; + + ubi = container_of(dev, struct ubi_device, dev); + ubi = ubi_get_device(ubi->ubi_num); + if (!ubi) + return -ENODEV; + + if (attr == &dev_mtd_trigger_scrub) { + if (kstrtoull(buf, 10, &scrub_sqnum)) { + ret = -EINVAL; + goto out; + } + if (!ubi->lookuptbl) { + pr_err("lookuptbl is null\n"); + ret = -ENOENT; + goto out; + } + ret = ubi_wl_scrub_all(ubi, scrub_sqnum); + if (ret == 0) + ret = count; + } + +out: + ubi_put_device(ubi); + return ret; +} + static void dev_release(struct device *dev) { struct ubi_device *ubi = container_of(dev, struct ubi_device, dev); @@ -438,7 +516,8 @@ static int uif_init(struct ubi_device *ubi) int i, err; dev_t dev; - sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); + scnprintf(ubi->ubi_name, sizeof(UBI_NAME_STR) + 5, + UBI_NAME_STR "%d", ubi->ubi_num); /* * Major numbers for the UBI character devices are allocated @@ -1376,7 +1455,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp) return 0; } - strcpy(buf, val); + strlcpy(buf, val, sizeof(buf)); /* Get rid of the final newline */ if (buf[len - 1] == '\n') @@ -1391,7 +1470,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp) } p = &mtd_dev_param[mtd_devs]; - strcpy(&p->name[0], tokens[0]); + strlcpy(&p->name[0], tokens[0], MTD_PARAM_LEN_MAX); token = tokens[1]; if (token) { diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index b57b84fb97d0..851d26e6f0e1 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -1056,6 +1056,15 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, dbg_io("write VID header to PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + /* + * Re-erase the PEB before using it. This should minimize any issues + * from decay of charge in this block. + */ + if (ubi->wl_is_inited) { + err = ubi_wl_re_erase_peb(ubi, pnum); + if (err) + return err; + } err = self_check_peb_ec_hdr(ubi, pnum); if (err) return err; @@ -1074,6 +1083,8 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); + if (!err && ubi->wl_is_inited) + ubi_wl_update_peb_sqnum(ubi, pnum, vid_hdr); return err; } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index a173eb707bdd..911fd3987537 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -170,6 +170,8 @@ struct ubi_vid_io_buf { * @u.list: link in the protection queue * @ec: erase counter * @pnum: physical eraseblock number + * @tagged_scrub_all: if the entry is tagged for scrub all + * @sqnum: The sequence number of the vol header. * * This data structure is used in the WL sub-system. Each physical eraseblock * has a corresponding &struct wl_entry object which may be kept in different @@ -182,6 +184,8 @@ struct ubi_wl_entry { } u; int ec; int pnum; + unsigned int tagged_scrub_all:1; + unsigned long long sqnum; }; /** @@ -625,6 +629,9 @@ struct ubi_device { struct task_struct *bgt_thread; int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; + bool scrub_in_progress; + atomic_t scrub_work_count; + int wl_is_inited; /* I/O sub-system's stuff */ long long flash_size; @@ -919,7 +926,12 @@ int ubi_is_erase_work(struct ubi_work *wrk); void ubi_refill_pools(struct ubi_device *ubi); int ubi_ensure_anchor_pebs(struct ubi_device *ubi); int ubi_bitflip_check(struct ubi_device *ubi, int pnum, int force_scrub); - +ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, + unsigned long long scrub_sqnum); +void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr); +unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi); +int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, int len); diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 139ee132bfbc..327f369d9fd0 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -54,7 +54,7 @@ static struct device_attribute attr_vol_upd_marker = static ssize_t vol_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) { - int ret; + int ret, size; struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); struct ubi_device *ubi; @@ -73,27 +73,36 @@ static ssize_t vol_attribute_show(struct device *dev, spin_unlock(&ubi->volumes_lock); if (attr == &attr_vol_reserved_ebs) - ret = sprintf(buf, "%d\n", vol->reserved_pebs); + ret = scnprintf(buf, sizeof(int), "%d\n", vol->reserved_pebs); else if (attr == &attr_vol_type) { const char *tp; - if (vol->vol_type == UBI_DYNAMIC_VOLUME) + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { tp = "dynamic"; - else + size = 8; + } else { tp = "static"; - ret = sprintf(buf, "%s\n", tp); + size = 7; + } + ret = scnprintf(buf, size, "%s\n", tp); } else if (attr == &attr_vol_name) - ret = sprintf(buf, "%s\n", vol->name); + ret = scnprintf(buf, vol->name_len + 1, "%s\n", + vol->name); else if (attr == &attr_vol_corrupted) - ret = sprintf(buf, "%d\n", vol->corrupted); + ret = scnprintf(buf, sizeof(int), "%d\n", + vol->corrupted); else if (attr == &attr_vol_alignment) - ret = sprintf(buf, "%d\n", vol->alignment); + ret = scnprintf(buf, sizeof(int), "%d\n", + vol->alignment); else if (attr == &attr_vol_usable_eb_size) - ret = sprintf(buf, "%d\n", vol->usable_leb_size); + ret = scnprintf(buf, sizeof(int), "%d\n", + vol->usable_leb_size); else if (attr == &attr_vol_data_bytes) - ret = sprintf(buf, "%lld\n", vol->used_bytes); + ret = scnprintf(buf, sizeof(unsigned long long), "%lld\n", + vol->used_bytes); else if (attr == &attr_vol_upd_marker) - ret = sprintf(buf, "%d\n", vol->upd_marker); + ret = scnprintf(buf, sizeof(int), "%d\n", + vol->upd_marker); else /* This must be a bug */ ret = -EINVAL; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 7def041bbe48..bc0b9ff9512e 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -89,6 +89,7 @@ #include #include #include +#include #include "ubi.h" #include "wl.h" @@ -488,6 +489,42 @@ out_free: return err; } +int ubi_wl_re_erase_peb(struct ubi_device *ubi, int pnum) +{ + int err; + struct ubi_wl_entry *e; + struct ubi_ec_hdr *ec_hdr; + + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + spin_unlock(&ubi->wl_lock); + + dbg_wl("Re-erase PEB %d, EC %u", e->pnum, e->ec); + + err = self_check_ec(ubi, e->pnum, e->ec); + if (err) + return -EINVAL; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_sync_erase(ubi, e->pnum, 0); + if (err < 0) + goto out_free; + + dbg_wl("re-erased PEB %d, EC %u", e->pnum, e->ec); + + ec_hdr->ec = cpu_to_be64(e->ec); + + err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); + if (err) + goto out_free; +out_free: + kfree(ec_hdr); + return err; +} + /** * serve_prot_queue - check if it is time to stop protecting PEBs. * @ubi: UBI device description object @@ -863,9 +900,19 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, } /* The PEB has been successfully moved */ - if (scrubbing) - ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", - e1->pnum, vol_id, lnum, e2->pnum); + if (scrubbing) { + spin_lock(&ubi->wl_lock); + if (e1->tagged_scrub_all) { + WARN_ON(atomic_read(&ubi->scrub_work_count) <= 0); + atomic_dec(&ubi->scrub_work_count); + e1->tagged_scrub_all = 0; + e2->tagged_scrub_all = 0; + } else { + ubi_msg(ubi, "scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", + e1->pnum, vol_id, lnum, e2->pnum); + } + spin_unlock(&ubi->wl_lock); + } ubi_free_vid_buf(vidb); spin_lock(&ubi->wl_lock); @@ -1233,6 +1280,7 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, retry: spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; + e->sqnum = UBI_UNKNOWN; if (e == ubi->move_from) { /* * User is putting the physical eraseblock which was selected to @@ -1269,6 +1317,20 @@ retry: } else if (in_wl_tree(e, &ubi->scrub)) { self_check_in_wl_tree(ubi, e, &ubi->scrub); rb_erase(&e->u.rb, &ubi->scrub); + + /* + * Since this PEB has been put we dont need to worry + * about it anymore + */ + if (e->tagged_scrub_all) { + int wrk_count; + + wrk_count = atomic_read(&ubi->scrub_work_count); + WARN_ON(wrk_count <= 0); + + atomic_dec(&ubi->scrub_work_count); + e->tagged_scrub_all = 0; + } } else if (in_wl_tree(e, &ubi->erroneous)) { self_check_in_wl_tree(ubi, e, &ubi->erroneous); rb_erase(&e->u.rb, &ubi->erroneous); @@ -1300,6 +1362,197 @@ retry: return err; } +/** + * ubi_wl_scrub_get_min_sqnum - Return the minimum sqnum of the used/pq/scrub. + * @ubi: UBI device description object + * + * This function returns the minimum sqnum of the PEB that are currently in use. + * + * Return the min sqnum if there are any used PEB's otherwise return ~(0) + * + */ +unsigned long long ubi_wl_scrub_get_min_sqnum(struct ubi_device *ubi) +{ + int i; + struct ubi_wl_entry *e, *tmp; + struct rb_node *node; + unsigned long long min_sqnum = ~0; + + spin_lock(&ubi->wl_lock); + + /* Go through the pq list */ + for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { + list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { + if (e->sqnum < min_sqnum) + min_sqnum = e->sqnum; + } + } + + /* Go through used PEB tree */ + for (node = rb_first(&ubi->used); node; node = rb_next(node)) { + e = rb_entry(node, struct ubi_wl_entry, u.rb); + self_check_in_wl_tree(ubi, e, &ubi->used); + if (e->sqnum < min_sqnum) + min_sqnum = e->sqnum; + } + /* Go through scrub PEB tree */ + for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { + e = rb_entry(node, struct ubi_wl_entry, u.rb); + self_check_in_wl_tree(ubi, e, &ubi->scrub); + if (e->sqnum < min_sqnum) + min_sqnum = e->sqnum; + } + spin_unlock(&ubi->wl_lock); + return min_sqnum; +} + +/** + * ubi_wl_update_peb_sqnum - Update the vol hdr sqnum of the PEB. + * @pnum: The PEB number. + * @vid_hdr: The vol hdr being written to the PEB. + * + */ +void ubi_wl_update_peb_sqnum(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr) +{ + struct ubi_wl_entry *e; + + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + e->sqnum = be64_to_cpu(vid_hdr->sqnum); + e->tagged_scrub_all = 0; + spin_unlock(&ubi->wl_lock); +} + +static int is_ubi_readonly(struct ubi_device *ubi) +{ + int is_readonly = 0; + + spin_lock(&ubi->wl_lock); + if (ubi->ro_mode || !ubi->thread_enabled || + ubi_dbg_is_bgt_disabled(ubi)) + is_readonly = 1; + spin_unlock(&ubi->wl_lock); + + return is_readonly; +} + +/** + * ubi_wl_scan_all - Scan all PEB's + * @ubi: UBI device description object + * @scrub_sqnum: The max seqnum of the PEB to scrub from the used/pq lists + * + * This function schedules all device PEBs for scrubbing if the sqnum of the + * vol hdr is less than the sqnum in the trigger. + * + * Return 0 in case of success, (negative) error code otherwise + * + */ +ssize_t ubi_wl_scrub_all(struct ubi_device *ubi, unsigned long long scrub_sqnum) +{ + struct rb_node *node; + struct ubi_wl_entry *e, *tmp; + int scrub_count = 0; + int total_scrub_count = 0; + int err, i; + + if (!ubi->lookuptbl) { + ubi_err(ubi, "lookuptbl is null"); + return -ENOENT; + } + + if (is_ubi_readonly(ubi)) { + ubi_err(ubi, "Cannot *Initiate* scrub:background thread disabled or readonly!"); + return -EROFS; + } + + /* Wait for all currently running work to be done! */ + down_write(&ubi->work_sem); + spin_lock(&ubi->wl_lock); + ubi_msg(ubi, "Scrub triggered sqnum = %llu!", scrub_sqnum); + + if (ubi->scrub_in_progress) { + ubi_err(ubi, "Scrub already in progress, ignoring the trigger"); + spin_unlock(&ubi->wl_lock); + up_write(&ubi->work_sem); /* Allow new work to start. */ + return -EBUSY; + } + ubi->scrub_in_progress = true; + + /* Go through scrub PEB tree and count pending */ + for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { + e = rb_entry(node, struct ubi_wl_entry, u.rb); + self_check_in_wl_tree(ubi, e, &ubi->scrub); + e->tagged_scrub_all = 1; + total_scrub_count++; + } + + /* Move all used pebs to scrub tree */ + node = rb_first(&ubi->used); + while (node != NULL) { + e = rb_entry(node, struct ubi_wl_entry, u.rb); + self_check_in_wl_tree(ubi, e, &ubi->used); + node = rb_next(node); + + if (e->sqnum > scrub_sqnum) + continue; + rb_erase(&e->u.rb, &ubi->used); + wl_tree_add(e, &ubi->scrub); + e->tagged_scrub_all = 1; + scrub_count++; + total_scrub_count++; + } + + /* Move all protected pebs to scrub tree */ + for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { + list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { + + if (e->sqnum > scrub_sqnum) + continue; + + list_del(&e->u.list); + wl_tree_add(e, &ubi->scrub); + e->tagged_scrub_all = 1; + scrub_count++; + total_scrub_count++; + } + } + + atomic_set(&ubi->scrub_work_count, total_scrub_count); + spin_unlock(&ubi->wl_lock); + up_write(&ubi->work_sem); /* Allow new work to start. */ + + /* + * Technically scrubbing is the same as wear-leveling, so it is done + * by the WL worker. + */ + err = ensure_wear_leveling(ubi, 0); + if (err) { + ubi_err(ubi, "Failed to start the WL worker err =%d", err); + return err; + } + ubi_msg(ubi, "Scheduled %d PEB's for scrubbing!", scrub_count); + ubi_msg(ubi, "Total PEB's for scrub = %d", total_scrub_count); + + /* Wait for scrub to finish */ + while (atomic_read(&ubi->scrub_work_count) > 0) { + /* Poll every second to check if the scrub work is done */ + msleep(1000); + + if (is_ubi_readonly(ubi)) { + ubi_err(ubi, "Cannot *Complete* scrub:background thread disabled or readonly!"); + return -EROFS; + } + wake_up_process(ubi->bgt_thread); + } + + spin_lock(&ubi->wl_lock); + ubi->scrub_in_progress = false; + spin_unlock(&ubi->wl_lock); + ubi_msg(ubi, "Done scrubbing %d PEB's!", scrub_count); + return 0; +} + /** * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. * @ubi: UBI device description object @@ -1753,7 +2006,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) ubi->max_ec = ai->max_ec; INIT_LIST_HEAD(&ubi->works); - sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); + scnprintf(ubi->bgt_name, sizeof(UBI_BGT_NAME_PATTERN)+2, + UBI_BGT_NAME_PATTERN, ubi->ubi_num); err = -ENOMEM; ubi->lookuptbl = kcalloc(ubi->peb_count, sizeof(void *), GFP_KERNEL); @@ -1786,6 +2040,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; + e->tagged_scrub_all = 0; + e->sqnum = aeb->sqnum; ubi_assert(e->ec >= 0); wl_tree_add(e, &ubi->free); @@ -1808,6 +2064,8 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; + e->tagged_scrub_all = 0; + e->sqnum = aeb->sqnum; ubi->lookuptbl[e->pnum] = e; if (!aeb->scrub) { @@ -1891,6 +2149,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) #ifdef CONFIG_MTD_UBI_FASTMAP ubi_ensure_anchor_pebs(ubi); #endif + ubi->wl_is_inited = 1; return 0; out_free: From 24ebbc28a20e73312534a8ea2144288db3f457dc Mon Sep 17 00:00:00 2001 From: Sai Chaitanya Kaveti Date: Tue, 9 Nov 2021 12:01:29 +0530 Subject: [PATCH 04/29] msm: mhi_dev: Added mutex lock in mhi_dev_write_channel mhi_dev_write_channel is called by diag channel. While processing it, reset interrupt is received from host. During the reset sequence the work queue mhi_sm_wq is getting destroyed in mhi_dev_sm_exit API. When the mhi_dev_write_channel is resumed, queuing of work is done as part of mhi_dev_notify_sm_event. Here, as the work queue is destroyed, crash occurred with a kernel null pointer deference error. This is a race condition between reset sequence and mhi_dev_notify_sm_event. To avoid this race condition added a mutex lock mhi_lock in mhi_dev_write_channel before calling mhi_dev_notify_sm_event. Change-Id: Idaf1c33c462b6d659f3e5ddb333afe9c6a967fac Signed-off-by: Sai Chaitanya Kaveti --- drivers/platform/msm/mhi_dev/mhi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/msm/mhi_dev/mhi.c b/drivers/platform/msm/mhi_dev/mhi.c index a6e5f1c52797..187e0c31c0cd 100644 --- a/drivers/platform/msm/mhi_dev/mhi.c +++ b/drivers/platform/msm/mhi_dev/mhi.c @@ -3535,13 +3535,16 @@ int mhi_dev_write_channel(struct mhi_req *wreq) * Expected usage is when there is a write * to the MHI core -> notify SM. */ + mutex_lock(&mhi_ctx->mhi_lock); mhi_log(MHI_MSG_CRITICAL, "Wakeup by chan:%d\n", ch->ch_id); rc = mhi_dev_notify_sm_event(MHI_DEV_EVENT_CORE_WAKEUP); if (rc) { pr_err("error sending core wakeup event\n"); + mutex_unlock(&mhi_ctx->mhi_lock); mutex_unlock(&mhi_ctx->mhi_write_test); return rc; } + mutex_unlock(&mhi_ctx->mhi_lock); } while (atomic_read(&mhi_ctx->is_suspended) && From 36d60ecc86d055ffe900f5b04faf503abb67e062 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Thu, 23 Dec 2021 11:36:51 +0530 Subject: [PATCH 05/29] cpuidle: lpm-levels: Log sched bias reason in trace Update the reason for cpuidle state selection as scheduler bias in case bias timer is set. Make use of unused next_event_us parameter to replace with cpu bias status. Change-Id: I42950b3d9d85da0ec5245c9cdbc7bcc5d609be3d Signed-off-by: Maulik Shah --- drivers/cpuidle/lpm-levels.c | 2 +- include/trace/events/trace_msm_low_power.h | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 57e6a2611567..50564ec5ef3f 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -776,7 +776,7 @@ static int cpu_power_select(struct cpuidle_device *dev, } done_select: - trace_cpu_power_select(best_level, sleep_us, latency_us, 0); + trace_cpu_power_select(best_level, sleep_us, latency_us, cpu->bias); trace_cpu_pred_select(idx_restrict_time ? 2 : (ipi_predicted ? 3 : (predicted ? 1 : 0)), predicted, htime); diff --git a/include/trace/events/trace_msm_low_power.h b/include/trace/events/trace_msm_low_power.h index d3f223a94b38..9b2ade049239 100644 --- a/include/trace/events/trace_msm_low_power.h +++ b/include/trace/events/trace_msm_low_power.h @@ -14,27 +14,27 @@ TRACE_EVENT(cpu_power_select, - TP_PROTO(int index, u32 sleep_us, u32 latency, u32 next_event_us), + TP_PROTO(int index, u32 sleep_us, u32 latency, u64 sched_bias), - TP_ARGS(index, sleep_us, latency, next_event_us), + TP_ARGS(index, sleep_us, latency, sched_bias), TP_STRUCT__entry( __field(int, index) __field(u32, sleep_us) __field(u32, latency) - __field(u32, next_event_us) + __field(u64, sched_bias) ), TP_fast_assign( __entry->index = index; __entry->sleep_us = sleep_us; __entry->latency = latency; - __entry->next_event_us = next_event_us; + __entry->sched_bias = sched_bias; ), - TP_printk("idx:%d sleep_time:%u latency:%u next_event:%u", + TP_printk("idx:%d sleep_time:%u latency:%u sched_bias:%lu", __entry->index, __entry->sleep_us, __entry->latency, - __entry->next_event_us) + __entry->sched_bias) ); TRACE_EVENT(cpu_pred_select, From 62bdfdbbd50af3b26866d96425f061584e674838 Mon Sep 17 00:00:00 2001 From: Sivakanth Vaka Date: Thu, 30 Sep 2021 17:10:59 +0530 Subject: [PATCH 06/29] msm: ipa: New APIs for PINE+PINE dual wifi support Add per instance APIs to support dual wifi attach. Change-Id: I9330382fde5e5c727dd5a619b863adbed9f0c661 Signed-off-by: Sivakanth Vaka Signed-off-by: Piyush Dhyani < --- drivers/platform/msm/ipa_fmwk/ipa_fmwk.c | 198 ++++++++++++++++++- include/linux/ipa_fmwk.h | 30 +++ include/linux/ipa_wdi3.h | 242 ++++++++++++++++++++++- 3 files changed, 467 insertions(+), 3 deletions(-) diff --git a/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c b/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c index 5bcd361a4a9d..418181b6fca8 100644 --- a/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c +++ b/drivers/platform/msm/ipa_fmwk/ipa_fmwk.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -318,6 +319,35 @@ struct ipa_fmwk_contex { bool (*ipa_wdi_is_tx1_used)(void); + int (*ipa_wdi_get_capabilities)(struct ipa_wdi_capabilities_out_params *out); + + int (*ipa_wdi_init_per_inst)(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out); + + int (*ipa_wdi_cleanup_per_inst)(u32 hdl); + + int (*ipa_wdi_reg_intf_per_inst)( + struct ipa_wdi_reg_intf_in_params *in); + + int (*ipa_wdi_dereg_intf_per_inst)(const char *netdev_name, u32 hdl); + + int (*ipa_wdi_conn_pipes_per_inst)(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out); + + int (*ipa_wdi_disconn_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_enable_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_disable_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_set_perf_profile_per_inst)(u32 hdl, struct ipa_wdi_perf_profile *profile); + + int (*ipa_wdi_create_smmu_mapping_per_inst)(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info); + + int (*ipa_wdi_release_smmu_mapping_per_inst)(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info); + /* ipa_gsb APIs*/ int (*ipa_bridge_init)(struct ipa_bridge_init_params *params, u32 *hdl); @@ -1248,7 +1278,19 @@ int ipa_fmwk_register_ipa_wdi3(const struct ipa_wdi3_data *in) || ipa_fmwk_ctx->ipa_wdi_get_stats || ipa_fmwk_ctx->ipa_get_wdi_version || ipa_fmwk_ctx->ipa_wdi_sw_stats - || ipa_fmwk_ctx->ipa_wdi_is_tx1_used) { + || ipa_fmwk_ctx->ipa_wdi_is_tx1_used + || ipa_fmwk_ctx->ipa_wdi_get_capabilities + || ipa_fmwk_ctx->ipa_wdi_init_per_inst + || ipa_fmwk_ctx->ipa_wdi_cleanup_per_inst + || ipa_fmwk_ctx->ipa_wdi_reg_intf_per_inst + || ipa_fmwk_ctx->ipa_wdi_dereg_intf_per_inst + || ipa_fmwk_ctx->ipa_wdi_conn_pipes_per_inst + || ipa_fmwk_ctx->ipa_wdi_disconn_pipes_per_inst + || ipa_fmwk_ctx->ipa_wdi_enable_pipes_per_inst + || ipa_fmwk_ctx->ipa_wdi_disable_pipes_per_inst + || ipa_fmwk_ctx->ipa_wdi_set_perf_profile_per_inst + || ipa_fmwk_ctx->ipa_wdi_create_smmu_mapping_per_inst + || ipa_fmwk_ctx->ipa_wdi_release_smmu_mapping_per_inst) { pr_err("ipa_wdi3 APIs were already initialized\n"); return -EPERM; } @@ -1271,7 +1313,26 @@ int ipa_fmwk_register_ipa_wdi3(const struct ipa_wdi3_data *in) ipa_fmwk_ctx->ipa_wdi_sw_stats = in->ipa_wdi_sw_stats; ipa_fmwk_ctx->ipa_get_wdi_version = in->ipa_get_wdi_version; ipa_fmwk_ctx->ipa_wdi_is_tx1_used = in->ipa_wdi_is_tx1_used; - + ipa_fmwk_ctx->ipa_wdi_get_capabilities = in->ipa_wdi_get_capabilities; + ipa_fmwk_ctx->ipa_wdi_init_per_inst = in->ipa_wdi_init_per_inst; + ipa_fmwk_ctx->ipa_wdi_cleanup_per_inst = in->ipa_wdi_cleanup_per_inst; + ipa_fmwk_ctx->ipa_wdi_reg_intf_per_inst = in->ipa_wdi_reg_intf_per_inst; + ipa_fmwk_ctx->ipa_wdi_dereg_intf_per_inst = + in->ipa_wdi_dereg_intf_per_inst; + ipa_fmwk_ctx->ipa_wdi_conn_pipes_per_inst = + in->ipa_wdi_conn_pipes_per_inst; + ipa_fmwk_ctx->ipa_wdi_disconn_pipes_per_inst = + in->ipa_wdi_disconn_pipes_per_inst; + ipa_fmwk_ctx->ipa_wdi_enable_pipes_per_inst = + in->ipa_wdi_enable_pipes_per_inst; + ipa_fmwk_ctx->ipa_wdi_disable_pipes_per_inst = + in->ipa_wdi_disable_pipes_per_inst; + ipa_fmwk_ctx->ipa_wdi_set_perf_profile_per_inst = + in->ipa_wdi_set_perf_profile_per_inst; + ipa_fmwk_ctx->ipa_wdi_create_smmu_mapping_per_inst = + in->ipa_wdi_create_smmu_mapping_per_inst; + ipa_fmwk_ctx->ipa_wdi_release_smmu_mapping_per_inst = + in->ipa_wdi_release_smmu_mapping_per_inst; pr_info("ipa_wdi3 registered successfully\n"); return 0; @@ -1411,6 +1472,139 @@ int ipa_wdi_release_smmu_mapping(u32 num_buffers, } EXPORT_SYMBOL(ipa_wdi_release_smmu_mapping); +int ipa_wdi_get_capabilities(struct ipa_wdi_capabilities_out_params *out) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_get_capabilities, out); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_get_capabilities); + +int ipa_wdi_init_per_inst(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_init_per_inst, + in, out); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_init_per_inst); + +int ipa_wdi_cleanup_per_inst(u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_cleanup_per_inst, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_cleanup_per_inst); + +int ipa_wdi_reg_intf_per_inst( + struct ipa_wdi_reg_intf_in_params *in) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_reg_intf_per_inst, + in); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_reg_intf_per_inst); + +int ipa_wdi_dereg_intf_per_inst(const char *netdev_name, u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_dereg_intf_per_inst, + netdev_name, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_dereg_intf_per_inst); + +int ipa_wdi_conn_pipes_per_inst(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_conn_pipes_per_inst, + in, out); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_conn_pipes_per_inst); + +int ipa_wdi_disconn_pipes_per_inst(u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_disconn_pipes_per_inst, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_disconn_pipes_per_inst); + +int ipa_wdi_enable_pipes_per_inst(u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_enable_pipes_per_inst, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_enable_pipes_per_inst); + +int ipa_wdi_disable_pipes_per_inst(u32 hdl) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_disable_pipes_per_inst, hdl); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_disable_pipes_per_inst); + +int ipa_wdi_set_perf_profile_per_inst(u32 hdl, + struct ipa_wdi_perf_profile *profile) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN(ipa_wdi_set_perf_profile_per_inst, + hdl, profile); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_set_perf_profile_per_inst); + +int ipa_wdi_create_smmu_mapping_per_inst(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN_DP(ipa_wdi_create_smmu_mapping_per_inst, + hdl, num_buffers, info); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_create_smmu_mapping_per_inst); + +int ipa_wdi_release_smmu_mapping_per_inst(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + int ret; + + IPA_FMWK_DISPATCH_RETURN_DP(ipa_wdi_release_smmu_mapping_per_inst, + hdl, num_buffers, info); + + return ret; +} +EXPORT_SYMBOL(ipa_wdi_release_smmu_mapping_per_inst); + int ipa_wdi_get_stats(struct IpaHwStatsWDIInfoData_t *stats) { int ret; diff --git a/include/linux/ipa_fmwk.h b/include/linux/ipa_fmwk.h index b286446c0430..6c66e4f6781d 100644 --- a/include/linux/ipa_fmwk.h +++ b/include/linux/ipa_fmwk.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018 - 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _IPA_FMWK_H_ @@ -207,6 +208,35 @@ struct ipa_wdi3_data { int (*ipa_get_wdi_version)(void); bool (*ipa_wdi_is_tx1_used)(void); + + int (*ipa_wdi_get_capabilities)(struct ipa_wdi_capabilities_out_params *out); + + int (*ipa_wdi_init_per_inst)(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out); + + int (*ipa_wdi_cleanup_per_inst)(u32 hdl); + + int (*ipa_wdi_reg_intf_per_inst)( + struct ipa_wdi_reg_intf_in_params *in); + + int (*ipa_wdi_dereg_intf_per_inst)(const char *netdev_name, u32 hdl); + + int (*ipa_wdi_conn_pipes_per_inst)(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out); + + int (*ipa_wdi_disconn_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_enable_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_disable_pipes_per_inst)(u32 hdl); + + int (*ipa_wdi_set_perf_profile_per_inst)(u32 hdl, struct ipa_wdi_perf_profile *profile); + + int (*ipa_wdi_create_smmu_mapping_per_inst)(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info); + + int (*ipa_wdi_release_smmu_mapping_per_inst)(u32 hdl, u32 num_buffers, + struct ipa_wdi_buffer_info *info); }; struct ipa_qdss_data { diff --git a/include/linux/ipa_wdi3.h b/include/linux/ipa_wdi3.h index ca07b74877a2..240ce576ae1b 100644 --- a/include/linux/ipa_wdi3.h +++ b/include/linux/ipa_wdi3.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018 - 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _IPA_WDI3_H_ @@ -19,15 +20,19 @@ #define IPA_WDI_MAX_SUPPORTED_SYS_PIPE 3 +typedef u32 ipa_wdi_hdl_t; + enum ipa_wdi_version { IPA_WDI_1, IPA_WDI_2, - IPA_WDI_3 + IPA_WDI_3, + IPA_WDI_VER_MAX }; #define IPA_WDI3_TX_DIR 1 #define IPA_WDI3_TX1_DIR 2 #define IPA_WDI3_RX_DIR 3 +#define IPA_WDI_INST_MAX (2) /** * struct ipa_wdi_init_in_params - wdi init input parameters @@ -43,6 +48,7 @@ struct ipa_wdi_init_in_params { #ifdef IPA_WAN_MSG_IPv6_ADDR_GW_LEN ipa_wdi_meter_notifier_cb wdi_notify; #endif + int inst_id; }; /** @@ -57,6 +63,7 @@ struct ipa_wdi_init_out_params { bool is_uC_ready; bool is_smmu_enabled; bool is_over_gsi; + ipa_wdi_hdl_t hdl; }; /** @@ -93,6 +100,7 @@ struct ipa_wdi_reg_intf_in_params { u32 meta_data; u32 meta_data_mask; u8 is_tx1_used; + ipa_wdi_hdl_t hdl; }; /** @@ -208,6 +216,7 @@ struct ipa_wdi_conn_in_params { struct ipa_wdi_pipe_setup_info tx; struct ipa_wdi_pipe_setup_info_smmu tx_smmu; } u_tx1; + ipa_wdi_hdl_t hdl; }; /** @@ -236,8 +245,30 @@ struct ipa_wdi_perf_profile { u32 max_supported_bw_mbps; }; + +/** + * struct ipa_wdi_capabilities - wdi capability parameters + * + * @num_of_instances: Number of WLAN instances supported. + */ +struct ipa_wdi_capabilities_out_params { + u8 num_of_instances; +}; + #if IS_ENABLED(CONFIG_IPA3) +/** + * ipa_wdi_get_capabilities - Client should call this function to + * know the WDI capabilities + * + * Note: Should not be called from atomic context and only + * after checking IPA readiness using ipa_register_ipa_ready_cb() + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_get_capabilities( + struct ipa_wdi_capabilities_out_params *out); + /** * ipa_wdi_init - Client should call this function to * init WDI IPA offload data path @@ -256,6 +287,18 @@ int ipa_wdi_init(struct ipa_wdi_init_in_params *in, */ bool ipa_wdi_is_tx1_used(void); +/** + * ipa_wdi_init_per_inst - Client should call this function to + * init WDI IPA offload data path + * + * Note: Should not be called from atomic context and only + * after checking IPA readiness using ipa_register_ipa_ready_cb() + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_init_per_inst(struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out); + /** * ipa_wdi_cleanup - Client should call this function to * clean up WDI IPA offload data path @@ -264,6 +307,17 @@ bool ipa_wdi_is_tx1_used(void); */ int ipa_wdi_cleanup(void); +/** + * ipa_wdi_cleanup_per_inst - Client should call this function to + * clean up WDI IPA offload data path + * + * @hdl: hdl to wdi client + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_cleanup_per_inst(ipa_wdi_hdl_t hdl); + + /** * ipa_wdi_reg_intf - Client should call this function to * register interface @@ -275,6 +329,17 @@ int ipa_wdi_cleanup(void); int ipa_wdi_reg_intf( struct ipa_wdi_reg_intf_in_params *in); +/** + * ipa_wdi_reg_intf_per_inst - Client should call this function to + * register interface + * + * Note: Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_reg_intf_per_inst( + struct ipa_wdi_reg_intf_in_params *in); + /** * ipa_wdi_dereg_intf - Client Driver should call this * function to deregister before unload and after disconnect @@ -283,6 +348,14 @@ int ipa_wdi_reg_intf( */ int ipa_wdi_dereg_intf(const char *netdev_name); +/** + * ipa_wdi_dereg_intf_per_inst - Client Driver should call this + * function to deregister before unload and after disconnect + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_dereg_intf_per_inst(const char *netdev_name, ipa_wdi_hdl_t hdl); + /** * ipa_wdi_conn_pipes - Client should call this * function to connect pipes @@ -297,6 +370,20 @@ int ipa_wdi_dereg_intf(const char *netdev_name); int ipa_wdi_conn_pipes(struct ipa_wdi_conn_in_params *in, struct ipa_wdi_conn_out_params *out); +/** + * ipa_wdi_conn_pipes_per_inst - Client should call this + * function to connect pipes + * + * @in: [in] input parameters from client + * @out: [out] output params to client + * + * Note: Should not be called from atomic context + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_conn_pipes_per_inst(struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out); + /** * ipa_wdi_disconn_pipes() - Client should call this * function to disconnect pipes @@ -307,6 +394,17 @@ int ipa_wdi_conn_pipes(struct ipa_wdi_conn_in_params *in, */ int ipa_wdi_disconn_pipes(void); +/** + * ipa_wdi_disconn_pipes_per_inst() - Client should call this + * function to disconnect pipes + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +int ipa_wdi_disconn_pipes_per_inst(ipa_wdi_hdl_t hdl); + /** * ipa_wdi_enable_pipes() - Client should call this * function to enable IPA offload data path @@ -317,6 +415,17 @@ int ipa_wdi_disconn_pipes(void); */ int ipa_wdi_enable_pipes(void); +/** + * ipa_wdi_enable_pipes_per_inst() - Client should call this + * function to enable IPA offload data path + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +int ipa_wdi_enable_pipes_per_inst(ipa_wdi_hdl_t hdl); + /** * ipa_wdi_disable_pipes() - Client should call this * function to disable IPA offload data path @@ -327,6 +436,17 @@ int ipa_wdi_enable_pipes(void); */ int ipa_wdi_disable_pipes(void); +/** + * ipa_wdi_disable_pipes_per_inst() - Client should call this + * function to disable IPA offload data path + * + * @hdl: hdl to wdi client + * Note: Should not be called from atomic context + * + * Returns: 0 on success, negative on failure + */ +int ipa_wdi_disable_pipes_per_inst(ipa_wdi_hdl_t hdl); + /** * ipa_wdi_set_perf_profile() - Client should call this function to * set IPA clock bandwidth based on data rates @@ -337,6 +457,18 @@ int ipa_wdi_disable_pipes(void); */ int ipa_wdi_set_perf_profile(struct ipa_wdi_perf_profile *profile); +/** + * ipa_wdi_set_perf_profile_per_inst() - Client should call this function to + * set IPA clock bandwidth based on data rates + * + * @hdl: hdl to wdi client + * @profile: [in] BandWidth profile to use + * + * Returns: 0 on success, negative on failure + */ +int ipa_wdi_set_perf_profile_per_inst(ipa_wdi_hdl_t hdl, + struct ipa_wdi_perf_profile *profile); + /** * ipa_wdi_create_smmu_mapping() - Create smmu mapping * @@ -347,6 +479,17 @@ int ipa_wdi_set_perf_profile(struct ipa_wdi_perf_profile *profile); int ipa_wdi_create_smmu_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info); +/** + * ipa_wdi_create_smmu_mapping_per_inst() - Create smmu mapping + * + * @hdl: hdl to wdi client + * @num_buffers: number of buffers + * @info: wdi buffer info + */ +int ipa_wdi_create_smmu_mapping_per_inst(ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info); + /** * ipa_wdi_release_smmu_mapping() - Release smmu mapping * @@ -357,6 +500,18 @@ int ipa_wdi_create_smmu_mapping(u32 num_buffers, int ipa_wdi_release_smmu_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info); +/** + * ipa_wdi_release_smmu_mapping_per_inst() - Release smmu mapping + * + * @hdl: hdl to wdi client + * @num_buffers: number of buffers + * + * @info: wdi buffer info + */ +int ipa_wdi_release_smmu_mapping_per_inst(ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info); + /** * ipa_wdi_get_stats() - Query WDI statistics * @stats: [inout] stats blob from client populated by driver @@ -394,12 +549,34 @@ int ipa_get_wdi_version(void); #else /* IS_ENABLED(CONFIG_IPA3) */ +/** + * ipa_wdi_get_capabilities - Client should call this function to + * know the WDI capabilities + * + * Note: Should not be called from atomic context and only + * after checking IPA readiness using ipa_register_ipa_ready_cb() + * + * @Return 0 on success, negative on failure + */ +int ipa_wdi_get_capabilities( + struct ipa_wdi_capabilities_out_params *out) +{ + return -EPERM; +} + static inline int ipa_wdi_init(struct ipa_wdi_init_in_params *in, struct ipa_wdi_init_out_params *out) { return -EPERM; } +static inline int ipa_wdi_init_per_inst( + struct ipa_wdi_init_in_params *in, + struct ipa_wdi_init_out_params *out) +{ + return -EPERM; +} + static inline int ipa_get_wdi_version(void) { return -EPERM; @@ -415,56 +592,119 @@ static inline int ipa_wdi_cleanup(void) return -EPERM; } +static inline int ipa_wdi_cleanup_per_inst(ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + static inline int ipa_wdi_reg_intf( struct ipa_wdi_reg_intf_in_params *in) { return -EPERM; } +static inline int ipa_wdi_reg_intf_per_inst( + struct ipa_wdi_reg_intf_in_params *in) +{ + return -EPERM; +} + static inline int ipa_wdi_dereg_intf(const char *netdev_name) { return -EPERM; } +static inline int ipa_wdi_dereg_intf_per_inst(const char *netdev_name, + ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + static inline int ipa_wdi_conn_pipes(struct ipa_wdi_conn_in_params *in, struct ipa_wdi_conn_out_params *out) { return -EPERM; } +static inline int ipa_wdi_conn_pipes_per_inst( + struct ipa_wdi_conn_in_params *in, + struct ipa_wdi_conn_out_params *out) +{ + return -EPERM; +} + static inline int ipa_wdi_disconn_pipes(void) { return -EPERM; } +static inline int ipa_wdi_disconn_pipes_per_inst(ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + + static inline int ipa_wdi_enable_pipes(void) { return -EPERM; } +static inline int ipa_wdi_enable_pipes_per_inst(ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + static inline int ipa_wdi_disable_pipes(void) { return -EPERM; } +static inline int ipa_wdi_disable_pipes_per_inst(ipa_wdi_hdl_t hdl) +{ + return -EPERM; +} + static inline int ipa_wdi_set_perf_profile( struct ipa_wdi_perf_profile *profile) { return -EPERM; } +static inline int ipa_wdi_set_perf_profile_per_inst( + ipa_wdi_hdl_t hdl, + struct ipa_wdi_perf_profile *profile) +{ + return -EPERM; +} + static inline int ipa_wdi_create_smmu_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info) { return -EPERM; } +static inline int ipa_wdi_create_smmu_mapping_per_inst( + ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + return -EPERM; +} + static inline int ipa_wdi_release_smmu_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info) { return -EPERM; } +static inline int ipa_wdi_release_smmu_mapping_per_inst( + ipa_wdi_hdl_t hdl, + u32 num_buffers, + struct ipa_wdi_buffer_info *info) +{ + return -EPERM; +} + static inline int ipa_wdi_get_stats(struct IpaHwStatsWDIInfoData_t *stats) { return -EPERM; From 59ba0d89511719def8ece50d822d58b92676a871 Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Wed, 5 Jan 2022 12:01:39 -0800 Subject: [PATCH 07/29] msm: ipa: match QMI request message length as per IDL Match the QMI request message max length as per IDL changes. Change-Id: I960c3f07cd738b6324fa4cd13ffe696870f33b4e Signed-off-by: Michael Adisumarta --- include/uapi/linux/ipa_qmi_service_v01.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h index 63dca0e5b116..55148fd4f494 100644 --- a/include/uapi/linux/ipa_qmi_service_v01.h +++ b/include/uapi/linux/ipa_qmi_service_v01.h @@ -2841,7 +2841,7 @@ struct ipa_move_nat_table_complt_ind_msg_v01 { #define QMI_IPA_MOVE_NAT_COMPLETE_IND_V01 0x0046 /* add for max length*/ -#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 210 +#define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 197 #define QMI_IPA_INIT_MODEM_DRIVER_RESP_MAX_MSG_LEN_V01 25 #define QMI_IPA_INDICATION_REGISTER_REQ_MAX_MSG_LEN_V01 16 #define QMI_IPA_INDICATION_REGISTER_RESP_MAX_MSG_LEN_V01 7 From 1508778d30d52f8f57f84e3c37c17e67e215ed5f Mon Sep 17 00:00:00 2001 From: Yogesh Lal Date: Thu, 12 Aug 2021 16:37:41 +0530 Subject: [PATCH 08/29] power: reset: qcom-dload-mode: Add support for EDL over PCIe Currently EDL mode works over USB, if PCIe is early enumerated on the device add support for EDL over PCIe. Change-Id: I051ef2a5fa4f63890a1044838dd870fa0195b0b7 Signed-off-by: Yogesh Lal --- drivers/power/reset/qcom-dload-mode.c | 35 +++++++++++++++++++++++++-- include/linux/qcom_scm.h | 1 + 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/drivers/power/reset/qcom-dload-mode.c b/drivers/power/reset/qcom-dload-mode.c index 9348723900ef..113db59c2aab 100644 --- a/drivers/power/reset/qcom-dload-mode.c +++ b/drivers/power/reset/qcom-dload-mode.c @@ -39,6 +39,7 @@ static bool enable_dump = IS_ENABLED(CONFIG_POWER_RESET_QCOM_DOWNLOAD_MODE_DEFAULT); static enum qcom_download_mode current_download_mode = QCOM_DOWNLOAD_NODUMP; static enum qcom_download_mode dump_mode = QCOM_DOWNLOAD_FULLDUMP; +static bool early_pcie_init_enable; static int set_download_mode(enum qcom_download_mode mode) { @@ -262,8 +263,10 @@ static int qcom_dload_reboot(struct notifier_block *this, unsigned long event, set_download_mode(QCOM_DOWNLOAD_NODUMP); if (cmd) { - if (!strcmp(cmd, "edl")) - set_download_mode(QCOM_DOWNLOAD_EDL); + if (!strcmp(cmd, "edl")) { + early_pcie_init_enable ? set_download_mode(QCOM_EDLOAD_PCI_MODE) + : set_download_mode(QCOM_DOWNLOAD_EDL); + } else if (!strcmp(cmd, "qcom_dload")) msm_enable_dump_mode(true); } @@ -311,6 +314,33 @@ static void store_kaslr_offset(void) static void store_kaslr_offset(void) {} #endif /* CONFIG_RANDOMIZE_BASE */ +static void check_pci_edl(struct device_node *np) +{ + void __iomem *mem; + uint32_t read_val; + int ret_l, ret_h, l, h, mask_value; + + mem = of_iomap(np, 0); + if (!mem) { + pr_info("Unable to map memory for DT property: %s\n", np->name); + return; + } + + read_val = __raw_readl(mem); + ret_l = of_property_read_u32_index(np, "qcom,boot-config-shift", 0, &l); + ret_h = of_property_read_u32_index(np, "qcom,boot-config-shift", 1, &h); + + if (!ret_l && !ret_h) { + mask_value = (read_val >> l) & GENMASK(h - l, 0); + if (mask_value == 5 || mask_value == 7) { + early_pcie_init_enable = true; + pr_info("Setting up EDL mode to PCIE\n"); + } + } + + iounmap(mem); +} + static int qcom_dload_probe(struct platform_device *pdev) { struct qcom_dload *poweroff; @@ -340,6 +370,7 @@ static int qcom_dload_probe(struct platform_device *pdev) poweroff->dload_dest_addr = map_prop_mem("qcom,msm-imem-dload-type"); store_kaslr_offset(); + check_pci_edl(pdev->dev.of_node); msm_enable_dump_mode(enable_dump); if (!enable_dump) diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index f4d883f158fd..d4ce6f0c7989 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -17,6 +17,7 @@ enum qcom_download_mode { QCOM_DOWNLOAD_NODUMP = 0x00, QCOM_DOWNLOAD_EDL = 0x01, + QCOM_EDLOAD_PCI_MODE = 0X04, QCOM_DOWNLOAD_FULLDUMP = 0x10, QCOM_DOWNLOAD_MINIDUMP = 0x20, }; From 701425a048c48323026e7a32598617146cbacc98 Mon Sep 17 00:00:00 2001 From: Shivali M S Date: Tue, 11 Jan 2022 10:32:18 +0530 Subject: [PATCH 09/29] msm: ep_pcie: Adding log for PHY version 7 Adding log as 5nm QMP for PHY version 7. Change-Id: Ia109d971e80d919b29dac5cd3bcbd02019bdfb18 Signed-off-by: Shivali M S --- drivers/platform/msm/ep_pcie/ep_pcie_phy.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_phy.c b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c index 7cb3c8832288..6741b0cced46 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_phy.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c @@ -31,6 +31,11 @@ void ep_pcie_phy_init(struct ep_pcie_dev_t *dev) "PCIe V%d: PHY V%d: Initializing 7nm QMP phy - 100MHz\n", dev->rev, dev->phy_rev); break; + case 7: + EP_PCIE_DBG(dev, + "PCIe V%d: PHY V%d: Initializing 5nm QMP phy - 100MHz\n", + dev->rev, dev->phy_rev); + break; default: EP_PCIE_ERR(dev, "PCIe V%d: Unexpected phy version %d is caught\n", From 8f54ba9ae7e38e315df32f5338665a1e44cd1a4b Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Mon, 30 Nov 2020 11:39:00 -0800 Subject: [PATCH 10/29] UPSTREAM: x86/pci: Fix the function type for check_reserved_t e820__mapped_all() is passed as a callback to is_mmconf_reserved(), which expects a function of type: typedef bool (*check_reserved_t)(u64 start, u64 end, unsigned type); However, e820__mapped_all() accepts enum e820_type as the last argument and this type mismatch trips indirect call checking with Clang's Control-Flow Integrity (CFI). As is_mmconf_reserved() only passes enum e820_type values for the type argument, change the typedef and the unused type argument in is_acpi_reserved() to enum e820_type to fix the type mismatch. Bug: 210705746 (cherry picked from commit 83321c335dccba262a57378361d63da96b8166d6) Reported-by: Sedat Dilek Suggested-by: Borislav Petkov Signed-off-by: Sami Tolvanen Signed-off-by: Borislav Petkov Link: https://lkml.kernel.org/r/20201130193900.456726-1-samitolvanen@google.com Change-Id: I92f78710ac55d5f567ad73053fc9f8c7cef68f71 (cherry picked from commit 7d1d5848183bd1d9086d0572f9af431d3ded407f) --- arch/x86/pci/mmconfig-shared.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 6fa42e9c4e6f..234998f196d4 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -425,7 +425,7 @@ static acpi_status find_mboard_resource(acpi_handle handle, u32 lvl, return AE_OK; } -static bool is_acpi_reserved(u64 start, u64 end, unsigned not_used) +static bool is_acpi_reserved(u64 start, u64 end, enum e820_type not_used) { struct resource mcfg_res; @@ -442,7 +442,7 @@ static bool is_acpi_reserved(u64 start, u64 end, unsigned not_used) return mcfg_res.flags; } -typedef bool (*check_reserved_t)(u64 start, u64 end, unsigned type); +typedef bool (*check_reserved_t)(u64 start, u64 end, enum e820_type type); static bool __ref is_mmconf_reserved(check_reserved_t is_reserved, struct pci_mmcfg_region *cfg, From f2bbf1b7baa5a0575d69d263a74dc943a8743606 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 16 Sep 2020 13:40:39 -0700 Subject: [PATCH 11/29] UPSTREAM: drivers core: Use sysfs_emit and sysfs_emit_at for show(device *...) functions commit aa838896d87af561a33ecefea1caa4c15a68bc47 upstream. Convert the various sprintf fmaily calls in sysfs device show functions to sysfs_emit and sysfs_emit_at for PAGE_SIZE buffer safety. Done with: $ spatch -sp-file sysfs_emit_dev.cocci --in-place --max-width=80 . And cocci script: $ cat sysfs_emit_dev.cocci @@ identifier d_show; identifier dev, attr, buf; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { <... return - sprintf(buf, + sysfs_emit(buf, ...); ...> } @@ identifier d_show; identifier dev, attr, buf; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { <... return - snprintf(buf, PAGE_SIZE, + sysfs_emit(buf, ...); ...> } @@ identifier d_show; identifier dev, attr, buf; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { <... return - scnprintf(buf, PAGE_SIZE, + sysfs_emit(buf, ...); ...> } @@ identifier d_show; identifier dev, attr, buf; expression chr; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { <... return - strcpy(buf, chr); + sysfs_emit(buf, chr); ...> } @@ identifier d_show; identifier dev, attr, buf; identifier len; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { <... len = - sprintf(buf, + sysfs_emit(buf, ...); ...> return len; } @@ identifier d_show; identifier dev, attr, buf; identifier len; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { <... len = - snprintf(buf, PAGE_SIZE, + sysfs_emit(buf, ...); ...> return len; } @@ identifier d_show; identifier dev, attr, buf; identifier len; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { <... len = - scnprintf(buf, PAGE_SIZE, + sysfs_emit(buf, ...); ...> return len; } @@ identifier d_show; identifier dev, attr, buf; identifier len; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { <... - len += scnprintf(buf + len, PAGE_SIZE - len, + len += sysfs_emit_at(buf, len, ...); ...> return len; } @@ identifier d_show; identifier dev, attr, buf; expression chr; @@ ssize_t d_show(struct device *dev, struct device_attribute *attr, char *buf) { ... - strcpy(buf, chr); - return strlen(buf); + return sysfs_emit(buf, chr); } Signed-off-by: Joe Perches Link: https://lore.kernel.org/r/3d033c33056d88bbe34d4ddb62afd05ee166ab9a.1600285923.git.joe@perches.com Cc: Lee Jones Signed-off-by: Greg Kroah-Hartman Bug: 182388481 [only the wakeup_stats.c portion - gregkh] (cherry picked from commit aa838896d87af561a33ecefea1caa4c15a68bc47) Signed-off-by: Greg Kroah-Hartman Change-Id: Ib6f1e608caae694532cfef2ab43092274630fb54 Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/wakeup_stats.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c index c7734914d914..5568e25d7c9c 100644 --- a/drivers/base/power/wakeup_stats.c +++ b/drivers/base/power/wakeup_stats.c @@ -42,7 +42,7 @@ static ssize_t active_time_ms_show(struct device *dev, ktime_t active_time = ws->active ? ktime_sub(ktime_get(), ws->last_time) : 0; - return sprintf(buf, "%lld\n", ktime_to_ms(active_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(active_time)); } static DEVICE_ATTR_RO(active_time_ms); @@ -57,7 +57,7 @@ static ssize_t total_time_ms_show(struct device *dev, active_time = ktime_sub(ktime_get(), ws->last_time); total_time = ktime_add(total_time, active_time); } - return sprintf(buf, "%lld\n", ktime_to_ms(total_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(total_time)); } static DEVICE_ATTR_RO(total_time_ms); @@ -73,7 +73,7 @@ static ssize_t max_time_ms_show(struct device *dev, if (active_time > max_time) max_time = active_time; } - return sprintf(buf, "%lld\n", ktime_to_ms(max_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(max_time)); } static DEVICE_ATTR_RO(max_time_ms); @@ -82,7 +82,7 @@ static ssize_t last_change_ms_show(struct device *dev, { struct wakeup_source *ws = dev_get_drvdata(dev); - return sprintf(buf, "%lld\n", ktime_to_ms(ws->last_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(ws->last_time)); } static DEVICE_ATTR_RO(last_change_ms); @@ -91,7 +91,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, { struct wakeup_source *ws = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", ws->name); + return sysfs_emit(buf, "%s\n", ws->name); } static DEVICE_ATTR_RO(name); @@ -106,7 +106,7 @@ static ssize_t prevent_suspend_time_ms_show(struct device *dev, prevent_sleep_time = ktime_add(prevent_sleep_time, ktime_sub(ktime_get(), ws->start_prevent_time)); } - return sprintf(buf, "%lld\n", ktime_to_ms(prevent_sleep_time)); + return sysfs_emit(buf, "%lld\n", ktime_to_ms(prevent_sleep_time)); } static DEVICE_ATTR_RO(prevent_suspend_time_ms); From 66a25bec608fc3d9debd0815eecc806466f45f2c Mon Sep 17 00:00:00 2001 From: Vivek Pernamitta Date: Wed, 1 Sep 2021 19:00:21 +0530 Subject: [PATCH 12/29] msm: mhi: Check ERRDBG registers for device status Add support to check RDDM cookie in ERRDBG1,ERRDBG2 and ERRDBG3 registers to debug the the bootup failure when device failed to enter MISSION mode. Change-Id: Idaf00c082ffda97805e958ae083a331d9f0909e9 Signed-off-by: Vivek Pernamitta Signed-off-by: Ramya SR --- drivers/bus/mhi/core/mhi_boot.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/bus/mhi/core/mhi_boot.c b/drivers/bus/mhi/core/mhi_boot.c index fa55209e409d..3f7d2a84748c 100644 --- a/drivers/bus/mhi/core/mhi_boot.c +++ b/drivers/bus/mhi/core/mhi_boot.c @@ -143,6 +143,17 @@ bool mhi_scan_rddm_cookie(struct mhi_controller *mhi_cntrl, u32 cookie) { int ret; u32 val; + int i; + bool result = false; + struct { + char *name; + u32 offset; + } error_reg[] = { + { "ERROR_DBG1", BHI_ERRDBG1 }, + { "ERROR_DBG2", BHI_ERRDBG2 }, + { "ERROR_DBG3", BHI_ERRDBG3 }, + { NULL }, + }; if (!mhi_cntrl->rddm_supported || !cookie) return false; @@ -152,15 +163,23 @@ bool mhi_scan_rddm_cookie(struct mhi_controller *mhi_cntrl, u32 cookie) if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) return false; - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_ERRDBG2, &val); - if (ret) - return false; + /* look for an RDDM cookie match in any of the error debug registers */ + for (i = 0; error_reg[i].name; i++) { + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, + error_reg[i].offset, &val); + if (ret) + break; + MHI_CNTRL_LOG("reg:%s value:0x%x\n", error_reg[i].name, val); - MHI_CNTRL_LOG("BHI_ERRDBG2 value:0x%x\n", val); - if (val == cookie) - return true; + if (!(val ^ cookie)) { + MHI_CNTRL_ERR("RDDM cookie found in %s\n", + error_reg[i].name); + return true; + } + } - return false; + MHI_CNTRL_ERR("RDDM cookie not found\n"); + return result; } EXPORT_SYMBOL(mhi_scan_rddm_cookie); From bc6f27677997446fd4a6d5571f52aee57d02d4ec Mon Sep 17 00:00:00 2001 From: Rohith Kollalsi Date: Thu, 11 Nov 2021 14:12:30 +0530 Subject: [PATCH 13/29] defconfig: Enable usb peripheral audio on sdxnightjar This change enables UAC1 and UAC2 protocol for USB peripheral audio on sdxnightjar. Change-Id: Icb804ab221a79ed2c581cebd7107a3a3055f40fd Signed-off-by: Rohith Kollalsi --- arch/arm/configs/vendor/sdxnightjar-debug.config | 2 -- arch/arm/configs/vendor/sdxnightjar.config | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/vendor/sdxnightjar-debug.config b/arch/arm/configs/vendor/sdxnightjar-debug.config index 9fa546194d67..3f49f7e71786 100644 --- a/arch/arm/configs/vendor/sdxnightjar-debug.config +++ b/arch/arm/configs/vendor/sdxnightjar-debug.config @@ -810,9 +810,7 @@ CONFIG_SND_DYNAMIC_MINORS=y # CONFIG_SND_AUDIO_GRAPH_CARD is not set # CONFIG_HID_PRODIKEYS is not set # CONFIG_USB_CONFIGFS_F_AUDIO_SRC is not set -# CONFIG_USB_CONFIGFS_F_UAC1 is not set # CONFIG_USB_CONFIGFS_F_UAC1_LEGACY is not set -# CONFIG_USB_CONFIGFS_F_UAC2 is not set # CONFIG_USB_CONFIGFS_F_MIDI is not set CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_MFD_SPMI_PMIC=y diff --git a/arch/arm/configs/vendor/sdxnightjar.config b/arch/arm/configs/vendor/sdxnightjar.config index 6b5bf80c596f..86a9b16bd507 100644 --- a/arch/arm/configs/vendor/sdxnightjar.config +++ b/arch/arm/configs/vendor/sdxnightjar.config @@ -850,12 +850,12 @@ CONFIG_SND_DYNAMIC_MINORS=y # CONFIG_SND_AUDIO_GRAPH_CARD is not set # CONFIG_HID_PRODIKEYS is not set # CONFIG_USB_CONFIGFS_F_AUDIO_SRC is not set -# CONFIG_USB_CONFIGFS_F_UAC1 is not set # CONFIG_USB_CONFIGFS_F_UAC1_LEGACY is not set -# CONFIG_USB_CONFIGFS_F_UAC2 is not set # CONFIG_USB_CONFIGFS_F_MIDI is not set # CONFIG_MEDIA_SUPPORT is not set # CONFIG_SND_SOC_TLV320AIC3X is not set +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y CONFIG_PINCTRL_QCOM_SPMI_PMIC=y CONFIG_MFD_SPMI_PMIC=y # CONFIG_QCOM_COINCELL is not set From 1298adebf7661a3e5fdd6e0c1f64c0457c9c3243 Mon Sep 17 00:00:00 2001 From: Sameer Saurabh Date: Wed, 10 Mar 2021 05:27:30 -0500 Subject: [PATCH 14/29] atlantic forwarding driver v1.1.22 - Fixed header file and warning complain - Atl-fwd: Fix name string for mac_phy stats in ethtool-dump - Atl-fwd: Minor additions to ethtool dump implementation - Atl-fwd: Add ethtool-dump parser - ATLDRV-1776: Fix for link flip during IPA ring reconfig - Atl-fwd: Update crash-dump structure to fixed size structure - Atl-fwd: Correct driver README file and export couple of APIs - Atl-fwd: Populate Action resolve table and Ring data in the ethtool-dump - ATLDRV-1766: Fix to update HWTS ring tail value to HW - Fix error in populating the extended stats - AQC113 register dump support - stats+crashdump implementation draft - Statistics and crash dump API proposal - factor out fwd stuff to drop headers dependencies - ATLDRV-1744] - Add FW version check in Atl-fwd driver - Move check for PTP UDP packets to separate function, Enable TX pad insert and Handle all FFs in ptp tx TS - ATLDRV-1582] - Link not Up after FLR reset - ATLDRV-1653] - Packet with vlan id 1 doesn't get received when rx vlan filter is disabled - ATLDRV-1659] - Failed to compile with CONFIG_ATLFWD_FWD=y flag on RHEL 8.3 kernel. Change-Id: Ie1c4a42f9938b0b4dfb26418a755010baadad011 Signed-off-by: Sameer Saurabh Git-commit: d86086216c6d511576d63dd216ab2cc1a8d46309 Git-repo: https://github.com/aquantia/linux-4.14-atlantic-forwarding Signed-off-by: Raihan Haider --- .../ethernet/aquantia/atlantic-fwd/atl2_fw.c | 9 + .../aquantia/atlantic-fwd/atl_common.h | 17 +- .../ethernet/aquantia/atlantic-fwd/atl_dump.h | 123 ++++++++ .../atlantic-fwd/atl_dump_parser/README | 21 ++ .../atl_dump_parser/atl_dump_parser.c | 296 ++++++++++++++++++ .../atl_dump_parser/atl_dump_parser.h | 10 + .../aquantia/atlantic-fwd/atl_ethtool.c | 52 +-- .../ethernet/aquantia/atlantic-fwd/atl_fw.c | 40 +++ .../ethernet/aquantia/atlantic-fwd/atl_fwd.c | 215 +++++++++++++ .../ethernet/aquantia/atlantic-fwd/atl_fwd.h | 39 ++- .../aquantia/atlantic-fwd/atl_fwdnl.c | 5 +- .../aquantia/atlantic-fwd/atl_fwdnl_params.c | 1 + .../ethernet/aquantia/atlantic-fwd/atl_hw.c | 46 ++- .../aquantia/atlantic-fwd/atl_hw_ptp.c | 2 + .../aquantia/atlantic-fwd/atl_macsec.c | 1 + .../ethernet/aquantia/atlantic-fwd/atl_main.c | 51 ++- .../ethernet/aquantia/atlantic-fwd/atl_ptp.c | 9 +- .../ethernet/aquantia/atlantic-fwd/atl_regs.h | 17 + .../ethernet/aquantia/atlantic-fwd/atl_ring.c | 30 +- .../aquantia/atlantic-fwd/atl_stats.h | 11 +- .../aquantia/atlantic-fwd/release_notes.txt | 35 +++ 21 files changed, 973 insertions(+), 57 deletions(-) create mode 100644 drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump.h create mode 100644 drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/README create mode 100644 drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c create mode 100644 drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl2_fw.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl2_fw.c index 59991ae6839a..ef8aed24a729 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl2_fw.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl2_fw.c @@ -973,6 +973,15 @@ int atl2_fw_init(struct atl_hw *hw) atl2_get_fw_version(hw); + /* Warn if FW is lower than 1.3.1 */ + if (((mcp->fw_rev >> 24) < 1) || + (((mcp->fw_rev >> 16) & 0xFFU) < 3) || + ((mcp->fw_rev & 0xFFFFU) < 1)) + atl_dev_warn("FW_version: %u.%u.%u, is not stable version", + (mcp->fw_rev >> 24), + ((mcp->fw_rev >> 16) & 0xFFU), + (mcp->fw_rev & 0xFFFFU)); + mcp->ops = &atl2_fw_ops; atl_dev_dbg("Detect ATL2FW %x\n", mcp->fw_rev); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h index cd0814608d29..29a7525bf4fa 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h @@ -20,7 +20,7 @@ #include #include -#define ATL_VERSION "1.1.18" +#define ATL_VERSION "1.1.22" struct atl_nic; @@ -32,8 +32,6 @@ struct atl_nic; #define ATL_MAX_QUEUES 8 -#include "atl_fwd.h" - struct atl_ptp; enum { @@ -414,6 +412,7 @@ void atl_update_global_stats(struct atl_nic *nic); void atl_set_loopback(struct atl_nic *nic, int idx, bool on); void atl_set_intr_mod(struct atl_nic *nic); void atl_update_ntuple_flt(struct atl_nic *nic, int idx); +int atl_vlan_promisc_status(struct net_device *ndev); void atl_set_vlan_promisc(struct atl_hw *hw, int promisc); int atl_hwsem_get(struct atl_hw *hw, int idx); void atl_hwsem_put(struct atl_hw *hw, int idx); @@ -424,18 +423,6 @@ int atl_msm_write(struct atl_hw *hw, uint32_t addr, uint32_t val); int atl_update_eth_stats(struct atl_nic *nic); void atl_adjust_eth_stats(struct atl_ether_stats *stats, struct atl_ether_stats *base, bool add); -void atl_fwd_release_rings(struct atl_nic *nic); -#if IS_ENABLED(CONFIG_ATLFWD_FWD) -enum atl_fwd_notify; -int atl_fwd_suspend_rings(struct atl_nic *nic); -int atl_fwd_resume_rings(struct atl_nic *nic); -void atl_fwd_notify(struct atl_nic *nic, enum atl_fwd_notify notif, void *data); -#else -static inline int atl_fwd_suspend_rings(struct atl_nic *nic) { return 0; } -static inline int atl_fwd_resume_rings(struct atl_nic *nic) { return 0; } -static inline void atl_fwd_notify(struct atl_nic *nic, - enum atl_fwd_notify notif, void *data) {} -#endif int atl_get_lpi_timer(struct atl_nic *nic, uint32_t *lpi_delay); void atl_refresh_rxfs(struct atl_nic *nic); void atl_schedule_work(struct atl_nic *nic); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump.h new file mode 100644 index 000000000000..f4a415dea82c --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Atlantic Network Driver + * + * Copyright (C) 2021 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ATL_DUMP_H_ +#define _ATL_DUMP_H_ + +enum atl_crash_dump_types { + atl_crash_dump_type_regs = 0, + atl_crash_dump_type_fwiface = 1, + atl_crash_dump_type_act_res = 2, + atl_crash_dump_type_ring = 3, +}; + +/** Some of the registers we can not read safely from the device. + * Those will not be read, with value 0xFFFFFFFF stored instead. + */ +struct atl_crash_dump_regs { + u32 type; + u32 length; + union { + u32 regs_data[0x9000/4]; + struct{ + u32 mif[0x1000/4]; + u32 pci[0x1000/4]; + u32 itr[0x1000/4]; + u32 com[0x1000/4]; + u32 mac_phy[0x1000/4]; + union { + u32 rx[0x2000/4]; + struct{ + u32 res1[0x700/4]; + u32 pb_ctrl; + u32 res2; + u32 pb_status; + u32 res3; + /* 0x5710 */ + struct{ + u32 pb_reg1; + u32 pb_reg2; + u32 pb_reg3; + } pb[8]; + u32 res4[0x100]; /* TODO */ + /* 0x5B00 */ + struct{ + u32 base_lo; + u32 base_hi; + u32 ctrl; + u32 head; + u32 tail; + u32 status; + u32 size; + u32 threshold; + } dma_desc[32]; + } layout; + }; + u32 tx[0x2000/4]; + } layout; + }; +}; + +struct atl_crash_dump_fwiface { + u32 type; + u32 length; + /* struct fw_interface_in */ + u32 fw_interface_in[0x1000/4]; + /* struct fw_interface_out */ + u32 fw_interface_out[0x1000/4]; +}; + +/* Record table size of 128 with each entry holding (tag, mask, action) */ +#define ATL_ACT_RES_TABLE_SIZE 384 +struct atl_crash_dump_act_res { + u32 type; + u32 length; + u32 act_res_data[ATL_ACT_RES_TABLE_SIZE]; +}; + +/* max_size = 'ring_size * (tx + rx) * 16 byte raw descriptor data' */ +#define ATL_MAX_RING_DESC_SIZE ATL_MAX_RING_SIZE * 32 +struct atl_crash_dump_ring { + u32 type; + u32 length; + u32 index; + u32 rx_head; + u32 rx_tail; + u32 tx_head; + u32 tx_tail; + u32 rx_ring_size; + u32 tx_ring_size; + u8 ring_data[ATL_MAX_RING_DESC_SIZE]; +}; + +/* This is an extensible structure to collect various debug data from + * AQC device + */ +struct atl_crash_dump { + u32 length; /* total crash structure length, in bytes */ + u32 sections_count; /* number of sections of type atl_crash_dump* following */ + u8 drv_version[16]; + u8 fw_version[16]; + union { + struct { + struct atl_crash_dump_regs regs; + struct atl_crash_dump_fwiface fwiface; + struct atl_crash_dump_act_res act_res; + struct atl_crash_dump_ring ring[ATL_MAX_QUEUES]; + } antigua; + struct { + struct atl_crash_dump_regs regs; + struct atl_crash_dump_ring ring[ATL_MAX_QUEUES]; + } atlantic; + }; +}; + + +#endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/README b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/README new file mode 100644 index 000000000000..fbf0aeec4ccf --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/README @@ -0,0 +1,21 @@ +The tool atl_dump_parser is used to parse the register/device dump collected +using "ethtool -d" command. Parser accepts dump in the binary format, hence the +dump must be collected using 'raw' option i.e., 'ethtool -d raw on' + +Use gcc to build the binary from sources, + gcc -o atl_dump_parser atl_dump_parser.c + +Example: +-------- +1. Collect ethtool dump from SUT machine + #ethtool -d enp1s0 raw on > ethd_dump + +2. Parse the dump + #ls + atl_dump_parser ethd_dump + #./atl_dump_parser ethd_dump + Parsing is completed successfully + #ls ethd_dump_parsed + action_resolver.txt atl_dump_parser ethd_dump fw_interface.txt registers.txt rings.txt + # + Parsed data will be copied to the text files under _parsed folder. diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c new file mode 100644 index 000000000000..00eb433bd596 --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include +#include +#include +#include "atl_dump_parser.h" +#include "../atl_dump.h" + +#define DEV_FILE "device.txt" +#define REG_FILE "registers.txt" +#define FW_IFACE_FILE "fw_interface.txt" +#define ACT_RES_FILE "action_resolver.txt" +#define RING_FILE "rings.txt" + +struct section_header { + u32 type; + u32 length; +}; + +char out_folder[64]; + +int dump_device_info(struct atl_crash_dump *crash_dump) +{ + char file_name[64]; + FILE *file; + + snprintf(file_name, 64, "%s/%s", out_folder, DEV_FILE); + file = fopen(file_name, "w"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + fprintf(file, "Driver version: %s\n", crash_dump->drv_version); + fprintf(file, "FW version: %s\n", crash_dump->fw_version); + + fclose(file); + + return 0; +} + +int dump_registers(struct atl_crash_dump_regs *reg) +{ + int block = 0x1000/4, i, offset; + char file_name[64]; + FILE *file; + + snprintf(file_name, 64, "%s/%s", out_folder, REG_FILE); + file = fopen(file_name, "w"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + /* Skip MIF block */ + + offset = block * 4; + fprintf(file, "PCI Registers\n"); + fprintf(file, "=============\n"); + for (i = 0; i < block; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.pci[i]); + + offset += block * 4; + fprintf(file, "\n\nInterrupt Registers\n"); + fprintf(file, "=====================\n"); + for (i = 0; i < block; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.itr[i]); + + offset += block * 4; + fprintf(file, "\n\nCOM Registers\n"); + fprintf(file, "===============\n"); + for (i = 0; i < block; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.com[i]); + + offset += block * 4; + fprintf(file, "\n\nMac Phy Registers\n"); + fprintf(file, "=====================\n"); + for (i = 0; i < block; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.mac_phy[i]); + + offset += block * 4; + fprintf(file, "\n\nRx Registers\n"); + fprintf(file, "===============\n"); + /* Rx block size is 0x2000 */ + for (i = 0; i < block * 2; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.rx[i]); + + offset += block * 2 * 4; + fprintf(file, "\n\nTx Registers\n"); + fprintf(file, "===============\n"); + /* Tx block size is 0x2000 */ + for (i = 0; i < block * 2; i++) + fprintf(file, "0x%08x: 0x%08x\n", offset + i * 4, reg->layout.tx[i]); + + fclose(file); + + return 0; +} + +int dump_fwiface(struct atl_crash_dump_fwiface *fwiface) +{ + int block = 0x1000/4, i; + char file_name[64]; + FILE *file; + + snprintf(file_name, 64, "%s/%s", out_folder, FW_IFACE_FILE); + file = fopen(file_name, "w"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + fprintf(file, "FW input interface\n"); + fprintf(file, "==================\n"); + for (i = 0; i < block; i += 4) + fprintf(file, "0x%08x:\t0x%08x 0x%08x 0x%08x 0x%08x\n", i, + fwiface->fw_interface_in[i], fwiface->fw_interface_in[i + 1], + fwiface->fw_interface_in[i + 2], fwiface->fw_interface_in[i + 3]); + + fprintf(file, "\n\nFW output interface\n"); + fprintf(file, "=======================\n"); + for (i = 0; i < block; i += 4) + fprintf(file, "0x%08x:\t0x%08x 0x%08x 0x%08x 0x%08x\n", i, + fwiface->fw_interface_out[i], fwiface->fw_interface_out[i + 1], + fwiface->fw_interface_out[i + 2], fwiface->fw_interface_out[i + 3]); + + fclose(file); + + return 0; +} + +int dump_act_res(struct atl_crash_dump_act_res *act_res) +{ + char file_name[64]; + int i, idx; + FILE *file; + + snprintf(file_name, 64, "%s/%s", out_folder, ACT_RES_FILE); + file = fopen(file_name, "w"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + fprintf(file, "RECORD RESOLVER TAG TAG Mask Action\n"); + fprintf(file, "==================================================\n"); + for (i = 0, idx = 0; i < ATL_ACT_RES_TABLE_SIZE / 3; i++) { + fprintf(file, "%d\t0x%08x\t0x%08x\t0x%08x\n", i, act_res->act_res_data[idx++], + act_res->act_res_data[idx++], act_res->act_res_data[idx++]); + } + + fclose(file); + + return 0; +} + +int dump_ring(struct atl_crash_dump_ring *ring) +{ + char file_name[64]; + FILE *file; + u32 *data; + int i; + + /* Assumption - file 'rings.txt' shouldn't exists */ + snprintf(file_name, 64, "%s/%s", out_folder, RING_FILE); + file = fopen(file_name, "a+"); + if (file == NULL) { + printf("Error opening file %s\n", file_name); + return -1; + } + + fprintf(file, "Ring Index %d\n", ring->index); + fprintf(file, "==============\n"); + fprintf(file, "Rx head = 0x%08x tail = 0x%08x\n", ring->rx_head, ring->rx_tail); + fprintf(file, "Tx head = 0x%08x tail = 0x%08x\n", ring->tx_head, ring->tx_tail); + fprintf(file, "HW Ring rx_size = %d tx_size = %d\n", ring->rx_ring_size, ring->tx_ring_size); + fprintf(file, "Rx Ring descriptor:\n"); + fprintf(file, "-------------------\n"); + data = (u32 *)ring->ring_data; + for (i = 0; i < ring->rx_ring_size; i += 4) + fprintf(file, "0x%08x:\t0x%08x 0x%08x 0x%08x 0x%08x\n", i, data[i], data[i + 1], + data[i + 2], data[i + 3]); + fprintf(file, "Tx Ring descriptor:\n"); + fprintf(file, "-------------------\n"); + data = (u32 *)ring->ring_data + ring->rx_ring_size; + for (i = 0; i < ring->tx_ring_size; i += 4) + fprintf(file, "0x%08x:\t0x%08x 0x%08x 0x%08x 0x%08x\n", i, data[i], data[i + 1], + data[i + 2], data[i + 3]); + fprintf(file, "\n"); + + fclose(file); + + return 0; + +} + +int main(int argc, char *argv[]) +{ + struct atl_crash_dump *crash_dump; + struct section_header *header; + int i, offset, ret = 0; + time_t t = time(NULL); + struct tm *tm_val; + FILE *input_file; + struct stat st; + char *buffer; + + if (argc < 2) { + printf("No intput file\n"); + return -1; + } + + if (stat(argv[1], &st) == -1) { + printf("File stat error\n"); + return -1; + } + + buffer = malloc(st.st_size); + if (!buffer) { + printf("Mem alloc error\n"); + return -1; + } + /* open the source file for reading */ + input_file = fopen(argv[1],"rb"); + if (input_file == NULL) { + ret = -1; + printf("Error opening file %s\n", argv[1]); + goto err; + } + + fread(buffer, sizeof(char), st.st_size, input_file); + fclose(input_file); + + tm_val = localtime(&t); + tm_val = NULL; + if (tm_val) + snprintf(out_folder, 64, "%s_%02d-%02d-%02d_%02d-%02d-%02d", argv[1], + tm_val->tm_mon + 1, (int) tm_val->tm_mday, 1900 + tm_val->tm_year, + tm_val->tm_hour, tm_val->tm_min, tm_val->tm_sec); + else + snprintf(out_folder, 64, "%s_parsed", argv[1]); + if (stat(out_folder, &st) == -1) { + if (mkdir(out_folder, 0644) == -1) { + ret = -1; + printf("Failed to creat folder` %s\n", out_folder); + goto err; + } + } + + crash_dump = (struct atl_crash_dump *)buffer; + ret = dump_device_info(crash_dump); + if (ret) + goto err; + + offset = offsetof(struct atl_crash_dump, antigua); + for (i = 0; i < crash_dump->sections_count; i++) { + header = (struct section_header *)(buffer + offset); + switch (header->type) { + case atl_crash_dump_type_regs: + ret = dump_registers((struct atl_crash_dump_regs *)(buffer + offset)); + if (ret) + goto err; + break; + case atl_crash_dump_type_fwiface: + ret = dump_fwiface((struct atl_crash_dump_fwiface *)(buffer + offset)); + if (ret) + goto err; + break; + case atl_crash_dump_type_act_res: + ret = dump_act_res((struct atl_crash_dump_act_res *)(buffer + offset)); + if (ret) + goto err; + break; + case atl_crash_dump_type_ring: + ret = dump_ring((struct atl_crash_dump_ring *)(buffer + offset)); + if (ret) + goto err; + break; + default: + printf("Parsing Error - invalid section\n"); + goto err; + } + offset += header->length; + } + +err: + free(buffer); + if (ret) + printf("Failed to parse the dump\n"); + else + printf("Parsing is completed successfully\n"); + + return 0; +} diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h new file mode 100644 index 000000000000..698036f78968 --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h @@ -0,0 +1,10 @@ +#ifndef __ATL_DUMP_PARSER +#define __ATL_DUMP_PARSER + +#define ATL_MAX_RING_SIZE (8192 - 8) +#define ATL_MAX_QUEUES 8 + +typedef unsigned int u32; +typedef unsigned char u8; + +#endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c index a365d2a9cc10..992f8e63feba 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c @@ -20,6 +20,7 @@ #include "atl_fwdnl.h" #include "atl_macsec.h" #include "atl_ptp.h" +#include "atl_fwd.h" static uint32_t atl_ethtool_get_link(struct net_device *ndev) { @@ -633,8 +634,8 @@ static const struct atl_stat_desc eth_stat_descs[] = { ATL_ETH_STAT(tx_pause, tx_pause), ATL_ETH_STAT(tx_ether_pkts, tx_ether_pkts), ATL_ETH_STAT(tx_ether_octets, tx_ether_octets), + ATL_ETH_STAT(tx_errors, tx_errors), ATL_ETH_STAT(rx_pause, rx_pause), - ATL_ETH_STAT(rx_ether_drops, rx_ether_drops), ATL_ETH_STAT(rx_ether_octets, rx_ether_octets), ATL_ETH_STAT(rx_ether_pkts, rx_ether_pkts), ATL_ETH_STAT(rx_ether_broacasts, rx_ether_broacasts), @@ -642,6 +643,13 @@ static const struct atl_stat_desc eth_stat_descs[] = { ATL_ETH_STAT(rx_ether_crc_align_errs, rx_ether_crc_align_errs), ATL_ETH_STAT(rx_filter_host, rx_filter_host), ATL_ETH_STAT(rx_filter_lost, rx_filter_lost), + ATL_ETH_STAT(rx_errors, rx_errors), + ATL_ETH_STAT(rx_drops, rx_drops), + ATL_ETH_STAT(rx_dma_packets, rx_dma_packets), + ATL_ETH_STAT(rx_dma_octets, rx_dma_octets), + ATL_ETH_STAT(rx_dma_drops, rx_dma_drops), + ATL_ETH_STAT(tx_dma_packets, tx_dma_packets), + ATL_ETH_STAT(tx_dma_octets, tx_dma_octets), }; #define ATL_PRIV_FLAG(_name, _bit) \ @@ -2711,9 +2719,7 @@ static void atl_refresh_rxf_desc(struct atl_nic *nic, atl_for_each_rxf_idx(desc, idx) desc->update_rxf(nic, idx); - atl_set_vlan_promisc(&nic->hw, (nic->ndev->flags & IFF_PROMISC) || - nic->rxf_vlan.promisc_count || - !nic->rxf_vlan.vlans_active); + atl_set_vlan_promisc(&nic->hw, atl_vlan_promisc_status(nic->ndev)); } void atl_refresh_rxfs(struct atl_nic *nic) @@ -2723,9 +2729,7 @@ void atl_refresh_rxfs(struct atl_nic *nic) atl_for_each_rxf_desc(desc) atl_refresh_rxf_desc(nic, desc); - atl_set_vlan_promisc(&nic->hw, (nic->ndev->flags & IFF_PROMISC) || - nic->rxf_vlan.promisc_count || - !nic->rxf_vlan.vlans_active); + atl_set_vlan_promisc(&nic->hw, atl_vlan_promisc_status(nic->ndev)); } static bool atl_vlan_pull_from_promisc(struct atl_nic *nic, uint32_t idx) @@ -2765,9 +2769,8 @@ static bool atl_vlan_pull_from_promisc(struct atl_nic *nic, uint32_t idx) } while (idx & ATL_VIDX_FREE); kfree(map); - atl_set_vlan_promisc(&nic->hw, (nic->ndev->flags & IFF_PROMISC) || - vlan->promisc_count || - !vlan->vlans_active); + atl_set_vlan_promisc(&nic->hw, atl_vlan_promisc_status(nic->ndev)); + return true; } @@ -2955,9 +2958,7 @@ int atl_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid) vlan->promisc_count++; if (pm_runtime_active(&nic->hw.pdev->dev)) atl_set_vlan_promisc(&nic->hw, - (ndev->flags & IFF_PROMISC) || - vlan->promisc_count || - !vlan->vlans_active); + atl_vlan_promisc_status(nic->ndev)); return 0; } @@ -2984,9 +2985,8 @@ update_vlan: atl_rxf_update_vlan(nic, idx); if (pm_runtime_active(&nic->hw.pdev->dev)) atl_set_vlan_promisc(&nic->hw, - (nic->ndev->flags & IFF_PROMISC) || - vlan->promisc_count || - !vlan->vlans_active); + atl_vlan_promisc_status(nic->ndev)); + return 0; } @@ -3027,9 +3027,9 @@ int atl_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid) update_vlan_promisc: if (pm_runtime_active(&nic->hw.pdev->dev)) - atl_set_vlan_promisc(&nic->hw, (ndev->flags & IFF_PROMISC) || - vlan->promisc_count || - !vlan->vlans_active); + atl_set_vlan_promisc(&nic->hw, + atl_vlan_promisc_status(nic->ndev)); + return 0; } @@ -3086,6 +3086,18 @@ static void atl_ethtool_complete(struct net_device *ndev) pm_runtime_put(&nic->hw.pdev->dev); } +static int atl_ethool_get_regs_len(struct net_device *ndev) +{ + return atl_get_crash_dump(ndev, NULL); +} + +static void atl_ethool_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *buf) +{ + regs->version = 0; + memset(buf, 0, regs->len); + atl_get_crash_dump(ndev, buf); +} + const struct ethtool_ops atl_ethtool_ops = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) .supported_coalesce_params = ETHTOOL_COALESCE_USECS | @@ -3128,4 +3140,6 @@ const struct ethtool_ops atl_ethtool_ops = { .set_wol = atl_set_wol, .begin = atl_ethtool_begin, .complete = atl_ethtool_complete, + .get_regs_len = atl_ethool_get_regs_len, + .get_regs = atl_ethool_get_regs, }; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c index cafd661c4dd8..7c86311a6df3 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fw.c @@ -1172,6 +1172,39 @@ int atl_update_thermal_flag(struct atl_hw *hw, int bit, bool val) static unsigned int atl_wdog_period = 1100; module_param_named(wdog_period, atl_wdog_period, uint, 0644); +static int atl_fw_rev_check_and_warn(int fw_rev) +{ + int major = (fw_rev >> 24) & 0xFFU; + bool ret = true; + + switch (major) { + case 2: + if ((((fw_rev >> 16) & 0xFFU) < 12) && + ((fw_rev & 0xFFFFU) < 12)) + ret = false; + break; + + case 3: + if ((((fw_rev >> 16) & 0xFFU) < 1) && + ((fw_rev & 0xFFFFU) < 102)) + ret = false; + + break; + + case 4: + if ((((fw_rev >> 16) & 0xFFU) < 2) && + ((fw_rev & 0xFFFFU) < 37)) + ret = false; + + break; + + default: + ret = false; + } + + return ret; +} + int atl_fw_init(struct atl_hw *hw) { uint32_t tries, reg, major; @@ -1186,6 +1219,13 @@ int atl_fw_init(struct atl_hw *hw) atl_dev_dbg("FW startup took %d ms\n", tries); major = (reg >> 24) & 0xff; + + /* Warn if FW is lower than 2.12.12, 3.1.102 and 4.2.37 */ + if (!atl_fw_rev_check_and_warn(reg)) + atl_dev_warn("FW_version: %u.%u.%u, is not stable version", + (reg >> 24), ((reg >> 16) & 0xFFU), + (reg & 0xFFFFU)); + if (!major || major > 3) { atl_dev_err("Unsupported FW major version: %u\n", major); return -EINVAL; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c index 2b5e899c6bf8..d46dfa2d9463 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.c @@ -12,6 +12,46 @@ #include #include "atl_common.h" #include "atl_desc.h" +#include "atl_fwd.h" +#include "atl_ring.h" + +static u32 atl_skip_reglist[] = { + /* PCIE */ + 0x1030, 0x1200, 0x1514, 0x1F00, 0x1F04, 0x1F20, 0x1F24, + + /* INTR Registers */ + 0x21A0, 0x21A4, 0x21A8, + + /* COM Registers */ + 0x30A0, 0x30A8, 0x3900, 0x3920, + + /* MAC PHY Registers */ + 0x4058, 0x4060, 0x4064, 0x40B0, 0x40B4, 0x40B8, 0x40BC, + 0x4120, 0x4140, 0x4144, 0x4190, 0x419C, 0x41A0, 0x41A4, + 0x41A8, 0x4270, 0x4288, 0x428C, 0x4290, 0x4294, 0x42E8, + 0x42EC, 0x434C, 0x4378, 0x4380, 0x4384, 0x4440, 0x4444, + 0x4970, 0x4B40, 0x4B44, 0x4B48, 0x4B4C, + + /* RX Registers */ + 0x5108, 0x54CC, 0x55B0, 0x55B4, 0x55C0, 0x55E0, 0x55E4, + 0x5640, 0x5718, 0x5728, 0x5738, 0x5748, 0x5758, 0x5768, + 0x5778, 0x5788, 0x5808, 0x580C, 0x5A10, 0x5B14, 0x5B34, + 0x5B54, 0x5B74, 0x5B94, 0x5BB4, 0x5BD4, 0x5BF4, 0x5C14, + 0x5C34, 0x5C54, 0x5C74, 0x5C94, 0x5CB4, 0x5CD4, 0x5CF4, + 0x5D14, 0x5D34, 0x5D54, 0x5D74, 0x5D94, 0x5DB4, 0x5DD4, + 0x5DF4, 0x5E14, 0x5E34, 0x5E54, 0x5E74, 0x5E94, 0x5EB4, + 0x5ED4, 0x5EF4, 0x6900, 0x6F00, 0x6F20, + + /* TX Registers */ + 0x7808, 0x7848, 0x784C, 0x78B0, 0x78B4, 0x7918, 0x7928, + 0x7938, 0x7948, 0x7958, 0x7968, 0x7978, 0x7988, 0x7A34, + 0x7B10, 0x7B14, 0x7B18, 0x7C14, 0x7C54, 0x7C94, 0x7CD4, + 0x7D14, 0x7D54, 0x7D94, 0x7DD4, 0x7E14, 0x7E54, 0x7E94, + 0x7ED4, 0x7F14, 0x7F54, 0x7F94, 0x7FD4, 0x8014, 0x8054, + 0x8094, 0x80D4, 0x8114, 0x8154, 0x8194, 0x81D4, 0x8214, + 0x8254, 0x8294, 0x82D4, 0x8314, 0x8354, 0x8394, 0x83D4, + 0x8900, 0x8904, 0x8F00, 0x8F20, +}; static const char *atl_fwd_dir_str(struct atl_fwd_ring *ring) { @@ -903,3 +943,178 @@ int atl_fwd_resume_rings(struct atl_nic *nic) err: return ret; } + +int atl_get_ext_stats(struct net_device *ndev, struct atl_ext_stats *stats) +{ + struct atl_nic *nic = netdev_priv(ndev); + + if (!stats) + return -EINVAL; + + atl_update_eth_stats(nic); + atl_update_global_stats(nic); + + memcpy(&stats->rx, &nic->stats.rx, sizeof(stats->rx)); + memcpy(&stats->tx, &nic->stats.tx, sizeof(stats->tx)); + memcpy(&stats->rx_fwd, &nic->stats.rx_fwd, sizeof(stats->rx_fwd)); + memcpy(&stats->eth, &nic->stats.eth, sizeof(stats->eth)); + + return 0; +} +EXPORT_SYMBOL(atl_get_ext_stats); + +static bool atl_skip_register(u32 reg) +{ + int i, count; + + count = sizeof(atl_skip_reglist) / sizeof(u32); + for (i = 0; i < count; i++) + if (atl_skip_reglist[i] == reg) + return true; + + return false; +} + +static int atl_get_crash_dump_regs(struct atl_hw *hw, struct atl_crash_dump_regs *section) +{ + int addr, index; + + section->type = atl_crash_dump_type_regs; + section->length = sizeof(struct atl_crash_dump_regs); + + /* prefill with 'skip' value */ + for (addr = 0; addr < sizeof(section->regs_data); addr++) + section->regs_data[addr] = 0xFFFFFFFF; + + for (addr = 0x1000, index = 0x1000 / 4; addr < 0x9000; addr += 4, index++) { + if (atl_skip_register(addr)) + continue; + section->regs_data[index] = atl_read(hw, addr); + } + + return section->length; +} + +static int atl_get_crash_dump_fwiface(struct atl_hw *hw, struct atl_crash_dump_fwiface *section) +{ + int i; + + section->type = atl_crash_dump_type_fwiface; + section->length = sizeof(struct atl_crash_dump_fwiface); + + for (i = 0; i < ARRAY_SIZE(section->fw_interface_in); i++) + section->fw_interface_in[i] = atl_read(hw, ATL2_MIF_SHARED_BUFFER_IN(i)); + + for (i = 0; i < ARRAY_SIZE(section->fw_interface_out); i++) + section->fw_interface_out[i] = atl_read(hw, ATL2_MIF_SHARED_BUFFER_OUT(i)); + + return section->length; +} + +static int atl_get_crash_dump_act_res(struct atl_hw *hw, struct atl_crash_dump_act_res *section) +{ + int i, idx, err; + + section->type = atl_crash_dump_type_act_res; + section->length = sizeof(struct atl_crash_dump_act_res); + + err = atl_hwsem_get(hw, ATL2_MCP_SEM_ACT_RSLVR); + if (err) { + printk("Failed to aquire act_res semaphore\n"); + goto ret; + } + + for (i = 0, idx = 0; i < ATL_ACT_RES_TABLE_SIZE / 3; i++) { + section->act_res_data[idx++] = atl_read(hw, ATL2_RPF_ACT_RSLVR_REQ_TAG(i)); + section->act_res_data[idx++] = atl_read(hw, ATL2_RPF_ACT_RSLVR_TAG_MASK(i)); + section->act_res_data[idx++] = atl_read(hw, ATL2_RPF_ACT_RSLVR_ACTN(i)); + } + + atl_hwsem_put(hw, ATL2_MCP_SEM_ACT_RSLVR); + +ret: + return section->length; +} + +static int atl_get_crash_dump_ring(struct atl_nic *nic, int idx, + struct atl_crash_dump_ring *section) +{ + int size = sizeof(struct atl_crash_dump_ring), offset; + struct atl_queue_vec *qvec = &nic->qvecs[idx]; + struct atl_hw_ring *hwring; + + if (!test_bit(ATL_ST_UP, &nic->hw.state)) { + memset(section, 0, size); + goto ret; + } + + section->type = atl_crash_dump_type_ring; + section->index = qvec->idx; + section->length = size; + section->rx_head = qvec->rx.head; + section->rx_tail = qvec->rx.tail; + section->tx_head = qvec->tx.head; + section->tx_tail = qvec->tx.tail; + hwring = &qvec->rx.hw; + section->rx_ring_size = hwring->size; + memcpy(section->ring_data, hwring->descs, hwring->size * sizeof(*hwring->descs)); + offset = hwring->size * sizeof(*hwring->descs); + hwring = &qvec->tx.hw; + section->tx_ring_size = hwring->size; + memcpy(section->ring_data + offset, hwring->descs, hwring->size * sizeof(*hwring->descs)); + +ret: + return size; +} + + +int atl_get_crash_dump(struct net_device *ndev, struct atl_crash_dump *crash_dump) +{ + struct atl_nic *nic = netdev_priv(ndev); + u32 fw_rev = nic->hw.mcp.fw_rev; + int recorded_sz = 0, i, nvecs; + u8 *section; + + if (!crash_dump) + return sizeof(struct atl_crash_dump); + + crash_dump->length = sizeof(*crash_dump); + crash_dump->sections_count = 0; + + strlcpy(crash_dump->drv_version, ATL_VERSION, sizeof(crash_dump->drv_version)); + snprintf(crash_dump->fw_version, sizeof(crash_dump->fw_version), + "%d.%d.%d", fw_rev >> 24, fw_rev >> 16 & 0xff, fw_rev & 0xffff); + + if (nic->hw.chip_id == ATL_ANTIGUA) + section = (void *)&crash_dump->antigua.regs; + else + section = (void *)&crash_dump->atlantic.regs; + + recorded_sz = atl_get_crash_dump_regs(&nic->hw, (void *)section); + crash_dump->sections_count++; + crash_dump->length += recorded_sz; + section += recorded_sz; + + if (nic->hw.chip_id == ATL_ANTIGUA) { + recorded_sz = atl_get_crash_dump_fwiface(&nic->hw, (void *)section); + crash_dump->sections_count++; + crash_dump->length += recorded_sz; + section += recorded_sz; + + recorded_sz = atl_get_crash_dump_act_res(&nic->hw, (void *)section); + crash_dump->sections_count++; + crash_dump->length += recorded_sz; + section += recorded_sz; + } + + nvecs = min_t(int, nic->nvecs, ATL_MAX_QUEUES); + for (i = 0; i < nvecs; i++) { + recorded_sz = atl_get_crash_dump_ring(nic, i, (void *)section); + crash_dump->sections_count++; + crash_dump->length += recorded_sz; + section += recorded_sz; + } + + return crash_dump->length; +} +EXPORT_SYMBOL(atl_get_crash_dump); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.h index 57b31320be5a..61b2aca5174c 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwd.h @@ -13,9 +13,10 @@ #define _ATL_FWD_H_ #include "atl_common.h" +#include "atl_dump.h" /* Each incompatible API change bumps the API version */ -#define ATL_FWD_API_VERSION 3 +#define ATL_FWD_API_VERSION 4 struct atl_fwd_event; @@ -405,4 +406,40 @@ enum atl_fwd_ring_state { ATL_FWR_ST_EVT_ENABLED = BIT(1), }; +#if IS_ENABLED(CONFIG_ATLFWD_FWD) +enum atl_fwd_notify; +void atl_fwd_release_rings(struct atl_nic *nic); +int atl_fwd_suspend_rings(struct atl_nic *nic); +int atl_fwd_resume_rings(struct atl_nic *nic); +void atl_fwd_notify(struct atl_nic *nic, enum atl_fwd_notify notif, void *data); +#else +static inline void atl_fwd_release_rings(struct atl_nic *nic) {} +static inline int atl_fwd_suspend_rings(struct atl_nic *nic) { return 0; } +static inline int atl_fwd_resume_rings(struct atl_nic *nic) { return 0; } +static inline void atl_fwd_notify(struct atl_nic *nic, + enum atl_fwd_notify notif, void *data) {} +#endif + +struct atl_ext_stats { + struct atl_rx_ring_stats rx; + struct atl_tx_ring_stats tx; + struct atl_rx_fwd_ring_stats rx_fwd; + + struct atl_ether_stats eth; +}; + +/** + * Direct statistics fetch call for the device + */ +int atl_get_ext_stats(struct net_device *ndev, struct atl_ext_stats *stats); + +/** + * Get the register and other crash related information from the device + * If @crash_dump is NULL, function will return the requested length of the buffer to + * store dump, in bytes + * if @crash_dump is not null, function will fill the buffer with crashdump + * sections filled, up to crash_dump->length + */ +int atl_get_crash_dump(struct net_device *ndev, struct atl_crash_dump *crash_dump); + #endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl.c index b9e86c4a300d..a11c42f6eec2 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl.c @@ -21,6 +21,7 @@ #include "atl_fwdnl.h" #include "atl_common.h" +#include "atl_fwd.h" #include "atl_fwdnl_params.h" #include "atl_ring.h" #include "atl_trace.h" @@ -1742,7 +1743,9 @@ static const struct nla_policy atlfwd_nl_policy[NUM_ATL_FWD_ATTR] = { [ATL_FWD_ATTR_QUEUE_INDEX] = { .type = NLA_S32 }, }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0) #define ATLFWD_NL_OP_POLICY(op_policy) .policy = op_policy #else #define ATLFWD_NL_OP_POLICY(op_policy) diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl_params.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl_params.c index 73b01c15e442..5da85ab5ccf3 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl_params.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_fwdnl_params.c @@ -13,6 +13,7 @@ #include #include "atl_common.h" +#include "atl_fwd.h" unsigned int atlfwd_nl_tx_clean_threshold_msec = 200; unsigned int atlfwd_nl_tx_clean_threshold_frac = 8; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c index 8f019ee9efac..e0635888b97b 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw.c @@ -114,6 +114,16 @@ static void atl_set_promisc(struct atl_hw *hw, bool enabled, bool allmulti) } } +int atl_vlan_promisc_status(struct net_device *ndev) +{ + struct atl_nic *nic = netdev_priv(ndev); + + return ndev->flags & IFF_PROMISC || + nic->rxf_vlan.promisc_count || + !nic->rxf_vlan.vlans_active || + !(ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER); +} + void atl_set_vlan_promisc(struct atl_hw *hw, int promisc) { atl_write_bit(hw, ATL_RX_VLAN_FLT_CTRL1, 1, !!promisc); @@ -746,7 +756,7 @@ void atl_start_hw_global(struct atl_nic *nic) (tpb_size * 32 * 66 / 100) << 16 | (tpb_size * 32 * 50 / 100)); /* 4-TC | Enable TPB */ - atl_set_bits(hw, ATL_TX_PBUF_CTRL1, BIT(8) | BIT(0)); + atl_set_bits(hw, ATL_TX_PBUF_CTRL1, BIT(8) | BIT(2) | BIT(0)); /* TX Buffer clk gate off */ if (hw->chip_id == ATL_ANTIGUA) atl_clear_bits(hw, ATL_TX_PBUF_CTRL1, BIT(5)); @@ -910,9 +920,7 @@ void atl_set_rx_mode(struct net_device *ndev) /* Enable promisc VLAN mode if IFF_PROMISC explicitly * requested or too many VIDs registered */ - atl_set_vlan_promisc(hw, - ndev->flags & IFF_PROMISC || nic->rxf_vlan.promisc_count || - !nic->rxf_vlan.vlans_active); + atl_set_vlan_promisc(hw, atl_vlan_promisc_status(ndev)); atl_set_promisc(hw, promisc_needed, is_multicast_enabled && all_multi_needed); @@ -1319,6 +1327,14 @@ static int __atl_fetch_msm2_stats(struct atl_hw *hw, __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_OCTETS_HI, ®2, hwsem_put); stats->tx_ether_octets = ((uint64_t)reg2 << 32) | reg; + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_FCS_ERRS_CNTR_LO, ®, hwsem_put); + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_FCS_ERRS_CNTR_HI, ®2, hwsem_put); + stats->tx_errors = ((uint64_t)reg2 << 32) | reg; + + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_FIFO_ERRS_CNTR_LO, ®, hwsem_put); + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_TX_FIFO_ERRS_CNTR_HI, ®2, hwsem_put); + stats->tx_errors += ((uint64_t)reg2 << 32) | reg; + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_PAUSE_LO, ®, hwsem_put); __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_PAUSE_HI, ®2, hwsem_put); stats->rx_pause = ((uint64_t)reg2 << 32) | reg; @@ -1346,6 +1362,13 @@ static int __atl_fetch_msm2_stats(struct atl_hw *hw, __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_ALIGN_ERRS_HI, ®2, hwsem_put); stats->rx_ether_crc_align_errs += ((uint64_t)reg2 << 32) | reg; + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_ERRS_CNTR_LO, ®, hwsem_put); + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_ERRS_CNTR_HI, ®2, hwsem_put); + stats->rx_errors = ((uint64_t)reg2 << 32) | reg; + + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_DROP_CNTR_LO, ®, hwsem_put); + __READ_MSM_OR_GOTO(ret, hw, ATL_MSM2_CTR_RX_DROP_CNTR_HI, ®2, hwsem_put); + stats->rx_drops = ((uint64_t)reg2 << 32) | reg; hwsem_put: atl_hwsem_put(hw, ATL_MCP_SEM_MSM); return ret; @@ -1373,7 +1396,19 @@ int atl_update_eth_stats(struct atl_nic *nic) if (ret) goto unlock_fw; - stats.rx_ether_drops = atl_read(hw, ATL_RX_DMA_STATS_CNT7); + stats.rx_dma_drops = atl_read(hw, ATL_RX_DMA_STATS_CNT7); + + stats.rx_dma_packets = atl_read(hw, ATL_RX_DMA_STATS_CNT1); + stats.rx_dma_packets += (uint64_t)atl_read(hw, ATL_RX_DMA_STATS_CNT2) << 32; + + stats.rx_dma_octets = atl_read(hw, ATL_RX_DMA_STATS_CNT3); + stats.rx_dma_octets += (uint64_t)atl_read(hw, ATL_RX_DMA_STATS_CNT4) << 32; + + stats.tx_dma_packets = atl_read(hw, ATL_TX_DMA_STATS_CNT1); + stats.tx_dma_packets += (uint64_t)atl_read(hw, ATL_RX_DMA_STATS_CNT2) << 32; + + stats.tx_dma_octets = atl_read(hw, ATL_TX_DMA_STATS_CNT3); + stats.tx_dma_octets += (uint64_t)atl_read(hw, ATL_TX_DMA_STATS_CNT4) << 32; /* capture debug counters*/ atl_write_bit(hw, ATL_RX_RPF_DBG_CNT_CTRL, 0x1f, 1); @@ -1685,4 +1720,3 @@ static void atl2_hw_new_rx_filter_promisc(struct atl_hw *hw, bool promisc, off_action); } - diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw_ptp.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw_ptp.c index fd2f0bd549b7..7f43a5ba5956 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_hw_ptp.c @@ -241,6 +241,8 @@ void hw_atl_extract_hwts(struct atl_hw *hw, struct atl_rx_desc_hwts_wb *hwts_wb, sec += tmp; tmp = (u64)((hwts_wb->sec_hw >> 22) & 0x3ff) << 38; sec += tmp; + if (sec == 0xffffffffffff && hwts_wb->ns == 0xffffffff) + return; ns = sec * NSEC_PER_SEC + hwts_wb->ns; if (timestamp) *timestamp = ns + hw->ptp_clk_offset; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_macsec.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_macsec.c index fea067b78f20..e9b1c4f9330e 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_macsec.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_macsec.c @@ -12,6 +12,7 @@ #include "atl_macsec.h" #if IS_ENABLED(CONFIG_MACSEC) && defined(NETIF_F_HW_MACSEC) #include "atl_common.h" +#include "atl_fwd.h" #include #include "macsec/macsec_api.h" diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c index c4a2be5768ba..b14b788bbdc0 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_main.c @@ -17,6 +17,7 @@ #include #include "atl_macsec.h" #include "atl_ptp.h" +#include "atl_fwd.h" const char atl_driver_name[] = "atlantic-fwd"; @@ -384,7 +385,7 @@ int atl_do_reset(struct atl_nic *nic) hw->mcp.ops->dump_cfg(hw); - atl_stop(nic, true); + atl_stop(nic, false); atl_fwd_suspend_rings(nic); @@ -765,9 +766,7 @@ static void atl_remove(struct pci_dev *pdev) atl_ptp_unregister(nic); unregister_netdev(nic->ndev); -#if IS_ENABLED(CONFIG_ATLFWD_FWD) atl_fwd_release_rings(nic); -#endif atl_clear_datapath(nic); @@ -948,6 +947,47 @@ static int atl_pm_runtime_idle(struct device *dev) return -EBUSY; } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 0)) +static void atl_reset_prepare(struct pci_dev *pdev) +{ + struct atl_nic *nic = pci_get_drvdata(pdev); + + pm_runtime_get_sync(&nic->hw.pdev->dev); + rtnl_lock(); + + if (netif_running(nic->ndev)) + atl_stop(nic, false); + + rtnl_unlock(); + pm_runtime_put_sync(&nic->hw.pdev->dev); +} + +static void atl_reset_done(struct pci_dev *pdev) +{ + struct atl_nic *nic = pci_get_drvdata(pdev); + + if (!nic) { + dev_err(&pdev->dev, "%s failed, device is unrecoverable\n", + __func__); + return; + } + + pm_runtime_get_sync(&nic->hw.pdev->dev); + rtnl_lock(); + + if (netif_running(nic->ndev)) + atl_start(nic); + + rtnl_unlock(); + pm_runtime_put_sync(&nic->hw.pdev->dev); +} + +static const struct pci_error_handlers atl_err_handlers = { + .reset_prepare = atl_reset_prepare, + .reset_done = atl_reset_done, +}; +#endif + const struct dev_pm_ops atl_pm_ops = { .suspend = atl_suspend_poweroff, .poweroff = atl_suspend_poweroff, @@ -968,6 +1008,11 @@ static struct pci_driver atl_pci_ops = { #ifdef CONFIG_PM .driver.pm = &atl_pm_ops, #endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 13, 0)) + .err_handler = &atl_err_handlers, +#endif + }; struct atl_thermal atl_def_thermal; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ptp.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ptp.c index 622ba980a20f..ee00257e66f9 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ptp.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ptp.c @@ -893,8 +893,13 @@ void atl_ptp_tx_hwtstamp(struct atl_nic *nic, u64 timestamp) return; } - timestamp += atomic_read(&ptp->offset_egress); - atl_ptp_convert_to_hwtstamp(&hwtstamp, timestamp); + if (!timestamp) { + atl_nic_err("ptp timestamp all-F from FW\n"); + memset(&hwtstamp, 0, sizeof(hwtstamp)); + } else { + timestamp += atomic_read(&ptp->offset_egress); + atl_ptp_convert_to_hwtstamp(&hwtstamp, timestamp); + } do { skb_tstamp_tx(skb, &hwtstamp); dev_kfree_skb_any(skb); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_regs.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_regs.h index cbe8fbf5e174..c3969a613256 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_regs.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_regs.h @@ -176,8 +176,17 @@ enum mcp_scratchpad { #define ATL_RX_RING_BUF_SIZE(ring) ATL_RING_OFFT(ring, 0x18) #define ATL_RX_RING_THRESH(ring) ATL_RING_OFFT(ring, 0x1c) +#define ATL_RX_DMA_STATS_CNT1 0x6800 +#define ATL_RX_DMA_STATS_CNT2 0x6804 +#define ATL_RX_DMA_STATS_CNT3 0x6808 +#define ATL_RX_DMA_STATS_CNT4 0x680C #define ATL_RX_DMA_STATS_CNT7 0x6818 +#define ATL_TX_DMA_STATS_CNT1 0x8800 +#define ATL_TX_DMA_STATS_CNT2 0x8804 +#define ATL_TX_DMA_STATS_CNT3 0x8808 +#define ATL_TX_DMA_STATS_CNT4 0x880C + /* TX @ 0x7000 */ #define ATL_TX_CTRL1 0x7000 #define ATL_TX_PO_CTRL1 0x7800 @@ -224,6 +233,10 @@ enum mcp_scratchpad { #define ATL_MSM_CTR_RX_ERRS 0x120 +#define ATL_MSM2_CTR_RX_DROP_CNTR_LO 0x158 +#define ATL_MSM2_CTR_RX_DROP_CNTR_HI 0x15C +#define ATL_MSM2_CTR_RX_ERRS_CNTR_LO 0x138 +#define ATL_MSM2_CTR_RX_ERRS_CNTR_HI 0x13C #define ATL_MSM2_CTR_RX_OCTETS_LO 0x100 #define ATL_MSM2_CTR_RX_OCTETS_HI 0x104 #define ATL_MSM2_CTR_RX_ALIGN_ERRS_LO 0x110 @@ -245,5 +258,9 @@ enum mcp_scratchpad { #define ATL_MSM2_CTR_TX_PKTS_LO 0x220 #define ATL_MSM2_CTR_TX_PKTS_HI 0x224 +#define ATL_MSM2_CTR_TX_FCS_ERRS_CNTR_LO 0x228 +#define ATL_MSM2_CTR_TX_FCS_ERRS_CNTR_HI 0x22C +#define ATL_MSM2_CTR_TX_FIFO_ERRS_CNTR_LO 0x238 +#define ATL_MSM2_CTR_TX_FIFO_ERRS_CNTR_HI 0x23C #endif diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c index b30a04c44ffd..31827eaa7fa5 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c @@ -244,6 +244,14 @@ static uint32_t atl_insert_context(struct atl_txbuf *txbuf, return tx_cmd; } +static bool atl_ptp_udp(struct sk_buff *skb) +{ + return (ip_hdr(skb)->version == 4) && + (ip_hdr(skb)->protocol == IPPROTO_UDP) && + ((udp_hdr(skb)->dest == htons(319)) || + (udp_hdr(skb)->dest == htons(320))); +} + netdev_tx_t atl_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct atl_nic *nic = netdev_priv(ndev); @@ -257,12 +265,9 @@ netdev_tx_t atl_start_xmit(struct sk_buff *skb, struct net_device *ndev) * and hardware PTP design of the chip. Otherwise ptp stream * will fail to sync */ - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) || - unlikely((ip_hdr(skb)->version == 4) && - (ip_hdr(skb)->protocol == IPPROTO_UDP) && - ((udp_hdr(skb)->dest == htons(319)) || - (udp_hdr(skb)->dest == htons(320)))) || - unlikely(eth_hdr(skb)->h_proto == htons(ETH_P_1588))) + if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) || + atl_ptp_udp(skb) || + (eth_hdr(skb)->h_proto == htons(ETH_P_1588)))) return atl_ptp_start_xmit(nic, skb); } @@ -448,7 +453,7 @@ static bool atl_checksum_workaround(struct sk_buff *skb, if (((desc->pkt_type & atl_rx_pkt_type_vlan_msk) == atl_rx_pkt_type_vlan) && - !(desc->rx_estat & atl_rx_estat_vlan_stripped)) + !(desc->rx_estat & atl_rx_estat_vlan_stripped)) ip_header_offset += sizeof(struct vlan_hdr); if ((desc->pkt_type & atl_rx_pkt_type_vlan_msk) == @@ -773,6 +778,9 @@ static int atl_fill_hwts_rx(struct atl_desc_ring *ring, uint32_t count, bool ato count--; } + wmb(); + atl_write(ring_hw(ring), ATL_RX_RING_TAIL(ring), ring->tail); + return 0; } @@ -1208,7 +1216,7 @@ int atl_clean_hwts_rx(struct atl_desc_ring *ring, int budget) uint32_t space = ring_space(ring); struct atl_rx_desc_hwts_wb *wb; struct atl_rxbuf *rxbuf; - u64 ns; + u64 ns = 0; DECLARE_SCRATCH_DESC(scratch); if (space >= refill_batch) @@ -2104,9 +2112,15 @@ void atl_stop_rings(struct atl_nic *nic) int atl_set_features(struct net_device *ndev, netdev_features_t features) { netdev_features_t changed = ndev->features ^ features; + struct atl_nic *nic = netdev_priv(ndev); ndev->features = features; + if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { + atl_set_vlan_promisc(&nic->hw, + atl_vlan_promisc_status(ndev)); + } + if (changed & NETIF_F_LRO) atl_set_lro(netdev_priv(ndev)); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_stats.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_stats.h index 070d078ed61f..5adfc9feed2b 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_stats.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_stats.h @@ -39,7 +39,7 @@ struct atl_rx_fwd_ring_stats { struct atl_tx_ring_stats { uint64_t packets; uint64_t bytes; - uint64_t tx_busy; + uint64_t tx_busy; /* number of times ring was full and tx failed */ uint64_t tx_restart; uint64_t dma_map_failed; }; @@ -55,8 +55,8 @@ struct atl_ether_stats { uint64_t tx_pause; uint64_t tx_ether_pkts; uint64_t tx_ether_octets; + uint64_t tx_errors; /* from MSM block */ uint64_t rx_pause; - uint64_t rx_ether_drops; uint64_t rx_ether_octets; uint64_t rx_ether_pkts; uint64_t rx_ether_broacasts; @@ -64,6 +64,13 @@ struct atl_ether_stats { uint64_t rx_ether_crc_align_errs; uint64_t rx_filter_host; uint64_t rx_filter_lost; + uint64_t rx_errors; /* from MSM block */ + uint64_t rx_drops; /* from MSM block */ + uint64_t rx_dma_packets; + uint64_t rx_dma_octets; + uint64_t rx_dma_drops; /* DMA level RX drops */ + uint64_t tx_dma_packets; + uint64_t tx_dma_octets; }; struct atl_global_stats { diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt b/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt index a15f3635f4e4..367bffeccd11 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt @@ -1,3 +1,38 @@ +Version 1.1.22 +Date 11/10/2021 +============== +- Fixed header file and warning complains + +Version 1.1.21 +Date 29/09/2021 +============== +- Atl-fwd: Fix name string for mac_phy stats in ethtool-dump +- Atl-fwd: Minor additions to ethtool dump implementation +- Atl-fwd: Add ethtool-dump parser +- ATLDRV-1776: Fix for link flip during IPA ring reconfig +- Atl-fwd: Update crash-dump structure to fixed size structure + +Version 1.1.20 +Date 23/08/2021 +============== +- Atl-fwd: Correct driver README file and export couple of APIs +- Atl-fwd: Populate Action resolve table and Ring data in the ethtool-dump +- ATLDRV-1766: Fix to update HWTS ring tail value to HW +- Fix error in populating the extended stats +- AQC113 register dump support +- stats+crashdump implementation draft +- Statistics and crash dump API proposal +- factor out fwd stuff to drop headers dependencies + +Version 1.1.19 +Date 12/07/2021 +============== +[ATLDRV-1744] - Add FW version check in Atl-fwd driver +- Move check for PTP UDP packets to separate function, Enable TX pad insert and Handle all FFs in ptp tx TS +[ATLDRV-1582] - Link not Up after FLR reset +[ATLDRV-1653] - Packet with vlan id 1 doesn't get received when rx vlan filter is disabled +[ATLDRV-1659] - Failed to compile with CONFIG_ATLFWD_FWD=y flag on RHEL 8.3 kernel + Version 1.1.18 Date 21/02/2021 ============== From 2993024f713c546355332f0316e4f2a9818fc4c2 Mon Sep 17 00:00:00 2001 From: Sameer Saurabh Date: Tue, 2 Nov 2021 10:59:19 +0530 Subject: [PATCH 15/29] atlantic forwarding driver v1.1.23 - Add License headers to atl_dump parser files. - Fixed cpus_read lock/unlock apis for latest kernel - Fixed the coalescing APIs for the latest kernel. Change-Id: Ic01e197195e715eca6388759298d54c4c51b0fe8 Signed-off-by: Sameer Saurabh Git-commit: 5721b086beed3aa34fd8b2c503b169b19b302664 Git-repo: https://github.com/aquantia/linux-4.14-atlantic-forwarding Signed-off-by: Raihan Haider --- .../ethernet/aquantia/atlantic-fwd/atl_common.h | 2 +- .../atlantic-fwd/atl_dump_parser/atl_dump_parser.c | 10 ++++++++++ .../atlantic-fwd/atl_dump_parser/atl_dump_parser.h | 10 ++++++++++ .../ethernet/aquantia/atlantic-fwd/atl_ethtool.c | 14 ++++++++++++++ .../net/ethernet/aquantia/atlantic-fwd/atl_ring.c | 8 ++++++++ .../aquantia/atlantic-fwd/release_notes.txt | 7 +++++++ 6 files changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h index 29a7525bf4fa..292c4295fa2b 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_common.h @@ -20,7 +20,7 @@ #include #include -#define ATL_VERSION "1.1.22" +#define ATL_VERSION "1.1.23" struct atl_nic; diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c index 00eb433bd596..ebad73ccec84 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.c @@ -1,3 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Atlantic Network Driver + * + * Copyright (C) 2021 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + #include #include #include diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h index 698036f78968..10ef5279f4cd 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_dump_parser/atl_dump_parser.h @@ -1,3 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Atlantic Network Driver + * + * Copyright (C) 2021 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + #ifndef __ATL_DUMP_PARSER #define __ATL_DUMP_PARSER diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c index 992f8e63feba..3c0fb9f6df03 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ethtool.c @@ -1181,8 +1181,15 @@ static int atl_set_priv_flags(struct net_device *ndev, uint32_t flags) return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) +static int atl_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *extack) +#else static int atl_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) +#endif { struct atl_nic *nic = netdev_priv(ndev); @@ -1193,8 +1200,15 @@ static int atl_get_coalesce(struct net_device *ndev, return 0; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) +static int atl_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *extack) +#else static int atl_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) +#endif { struct atl_nic *nic = netdev_priv(ndev); diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c index 31827eaa7fa5..685103c4d544 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/atl_ring.c @@ -1397,7 +1397,11 @@ static void atl_calc_affinities(struct atl_nic *nic) int i; unsigned int cpu; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + cpus_read_lock(); +#else get_online_cpus(); +#endif cpu = cpumask_first(cpu_online_mask); for (i = 0; i < nic->nvecs; i++) { @@ -1414,7 +1418,11 @@ static void atl_calc_affinities(struct atl_nic *nic) cpumask_set_cpu(cpu, cpumask); cpu = cpumask_next(cpu, cpu_online_mask); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + cpus_read_unlock(); +#else put_online_cpus(); +#endif } void atl_init_qvec(struct atl_nic *nic, struct atl_queue_vec *qvec, int idx) diff --git a/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt b/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt index 367bffeccd11..50c5e78a7362 100644 --- a/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt +++ b/drivers/net/ethernet/aquantia/atlantic-fwd/release_notes.txt @@ -1,3 +1,10 @@ +Version 1.1.23 +Date 02/11/2021 +============== +- Add License headers to atl_dump parser files +- Fixed cpus_read lock/unlock apis for latest kernel +- Fixed the coalescing APIs for the latest kernel + Version 1.1.22 Date 11/10/2021 ============== From 0f82107638869b1807fff5eba39e16a78710bff5 Mon Sep 17 00:00:00 2001 From: Puranam V G Tejaswi Date: Fri, 7 Jan 2022 02:20:41 +0530 Subject: [PATCH 16/29] msm: kgsl: Perform cache flush on the pages obtained using get_user_pages() Consider a scenario where user allocates anonymous memory but does not write to it. Here the physical pages are not yet allocated. Now when this memory is requested to be imported, a list of newly allocated zero pages is obtained using get_user_pages(). Currently cache flush is not done for these pages and hence GPU sees stale data. Fix this by performing cache flush on these pages. Change-Id: Id1e8aa20e8a9de112761732ed92f30c01088840b Signed-off-by: Puranam V G Tejaswi --- drivers/gpu/msm/kgsl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index f932ae24d64c..20ce446f7391 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2008-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -2517,6 +2518,15 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, unsigned long useraddr) ret = sg_alloc_table_from_pages(memdesc->sgt, pages, npages, 0, memdesc->size, GFP_KERNEL); + + if (ret) + goto out; + + ret = kgsl_cache_range_op(memdesc, 0, memdesc->size, + KGSL_CACHE_OP_FLUSH); + + if (ret) + sg_free_table(memdesc->sgt); out: if (ret) { for (i = 0; i < npages; i++) From af60234af1a24a47056d82b349670273281de702 Mon Sep 17 00:00:00 2001 From: Pooventhiran G Date: Thu, 13 Jan 2022 19:07:21 +0530 Subject: [PATCH 17/29] bridge: Port Hy-Fi bridging hooks Changes to port Hy-Fi bridging hooks and to export br_fdb_find_rcu symbol. Change-Id: Ife2a9ac76e02fff56b8c4acaa97794df7e98863d Signed-off-by: Muthuchamy Kumar Signed-off-by: Pooventhiran G --- include/linux/if_bridge.h | 12 ++++++++++++ net/bridge/Kconfig | 12 ++++++++++++ net/bridge/br.c | 6 ++++++ net/bridge/br_device.c | 6 ++++++ net/bridge/br_fdb.c | 7 +++++++ net/bridge/br_input.c | 20 +++++++++++++++++++- net/bridge/br_netlink.c | 3 +++ net/bridge/br_private.h | 19 +++++++++++++++++++ 8 files changed, 84 insertions(+), 1 deletion(-) diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 6009f14c9cfc..9e2ad3b81690 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -50,6 +50,11 @@ struct br_ip_list { #define BR_DEFAULT_AGEING_TIME (300 * HZ) +//Handle HYFI_BRIDGING hooks related stuffs, only if HYFI_BRIDGE_HOOKS is defined +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +struct net_bridge_port; +#endif + extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *)); #ifdef CONFIG_ENABLE_SFE @@ -151,4 +156,11 @@ br_port_flag_is_set(const struct net_device *dev, unsigned long flag) } #endif +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +typedef void (br_notify_hook_t)(int group, int event, const void *ptr); +extern br_notify_hook_t __rcu *br_notify_hook; +typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src, + struct sk_buff *skb); +extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook; +#endif #endif diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig index e4fb050e2078..a9302806d8f6 100644 --- a/net/bridge/Kconfig +++ b/net/bridge/Kconfig @@ -61,3 +61,15 @@ config BRIDGE_VLAN_FILTERING Say N to exclude this support and reduce the binary size. If unsure, say Y. + +config HYFI_BRIDGE_HOOKS + bool "Port Hy-Fi Bridging Hooks" + depends on BRIDGE + depends on QGKI + default y + help + If you say Y here, then the Port Hy-Fi Bridging Hooks will be enabled. + + Say N to exclude this support. + + If unsure, say Y. diff --git a/net/bridge/br.c b/net/bridge/br.c index cccbb9bf3ca4..17e7a0fd9592 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -394,6 +394,12 @@ static void __exit br_deinit(void) br_fdb_fini(); } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +/* Hook for bridge event notifications */ +br_notify_hook_t __rcu *br_notify_hook __read_mostly; +EXPORT_SYMBOL(br_notify_hook); +#endif + module_init(br_init) module_exit(br_deinit) MODULE_LICENSE("GPL"); diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 0dd8984a261d..d30c243c1442 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -81,6 +81,12 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) if (is_broadcast_ether_addr(dest)) { br_flood(br, skb, BR_PKT_BROADCAST, false, true); } else if (is_multicast_ether_addr(dest)) { +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + br_multicast_handle_hook_t *multicast_handle_hook = + rcu_dereference(br_multicast_handle_hook); + if (!__br_get(multicast_handle_hook, true, NULL, skb)) + goto out; +#endif if (unlikely(netpoll_tx_running(dev))) { br_flood(br, skb, BR_PKT_MULTICAST, false, true); goto out; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index b1d3248c0252..bf80a5deb087 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -147,6 +147,10 @@ struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br, return fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +EXPORT_SYMBOL(br_fdb_find_rcu); +#endif + /* When a static FDB entry is added, the mac address from the entry is * added to the bridge private HW address list and all required ports * are then updated with the new information. @@ -709,6 +713,9 @@ static void fdb_notify(struct net_bridge *br, kfree_skb(skb); goto errout; } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + __br_notify(RTNLGRP_NEIGH, type, fdb); +#endif rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); return; errout: diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index c30d4837d66d..4e7551358682 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -29,7 +29,13 @@ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) return netif_receive_skb(skb); } -static int br_pass_frame_up(struct sk_buff *skb) +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +/* Hook for external Multicast handler */ +br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; +EXPORT_SYMBOL(br_multicast_handle_hook); +#endif + +int br_pass_frame_up(struct sk_buff *skb) { struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; struct net_bridge *br = netdev_priv(brdev); @@ -66,6 +72,10 @@ static int br_pass_frame_up(struct sk_buff *skb) br_netif_receive_skb); } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +EXPORT_SYMBOL(br_pass_frame_up); +#endif + /* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { @@ -75,6 +85,9 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb struct net_bridge_mdb_entry *mdst; bool local_rcv, mcast_hit = false; struct net_bridge *br; +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + br_multicast_handle_hook_t *multicast_handle_hook; +#endif u16 vid = 0; if (!p || p->state == BR_STATE_DISABLED) @@ -128,6 +141,11 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb switch (pkt_type) { case BR_PKT_MULTICAST: +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + multicast_handle_hook = rcu_dereference(br_multicast_handle_hook); + if (!__br_get(multicast_handle_hook, true, p, skb)) + goto out; +#endif mdst = br_mdb_get(br, skb, vid); if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && br_multicast_querier_exists(br, eth_hdr(skb))) { diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 8a664148f57a..6e214587c049 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -499,6 +499,9 @@ void br_ifinfo_notify(int event, const struct net_bridge *br, kfree_skb(skb); goto errout; } +#ifdef CONFIG_HYFI_BRIDGE_HOOKS + __br_notify(RTNLGRP_LINK, event, port); +#endif rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); return; errout: diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index c83d3a954b5f..e10e5def08b9 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -18,6 +18,9 @@ #include #include #include +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +#include +#endif #define BR_HASH_BITS 8 #define BR_HASH_SIZE (1 << BR_HASH_BITS) @@ -625,6 +628,9 @@ void br_manage_promisc(struct net_bridge *br); int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); /* br_input.c */ +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +int br_pass_frame_up(struct sk_buff *skb); +#endif int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); rx_handler_result_t br_handle_frame(struct sk_buff **pskb); @@ -1266,4 +1272,17 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, u16 vid, struct net_bridge_port *p, struct nd_msg *msg); struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m); +#ifdef CONFIG_HYFI_BRIDGE_HOOKS +#define __br_get(__hook, __default, __args ...) \ + ({ typeof(__hook) HOOK_ = (__hook); \ + (HOOK_ ? (HOOK_(__args)) : (__default)); }) + +static inline void __br_notify(int group, int type, const void *data) +{ + br_notify_hook_t *notify_hook = rcu_dereference(br_notify_hook); + + if (notify_hook) + notify_hook(group, type, data); +} +#endif #endif From 1b9a02d411bff45e00bd0de00ba2c9cdf7e46df1 Mon Sep 17 00:00:00 2001 From: Srinivasarao Pathipati Date: Thu, 20 Jan 2022 21:37:44 +0530 Subject: [PATCH 18/29] pinctrl: qcom: correct function name for pingroup28 use qup11_f1 instance for i2c usecase. Change-Id: I639b1097abfa7d1cc3edcd316c3922bb9d65850f Signed-off-by: Srinivasarao Pathipati --- drivers/pinctrl/qcom/pinctrl-blair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/qcom/pinctrl-blair.c b/drivers/pinctrl/qcom/pinctrl-blair.c index 8d4b6a3f83ff..39ee6e4674c0 100644 --- a/drivers/pinctrl/qcom/pinctrl-blair.c +++ b/drivers/pinctrl/qcom/pinctrl-blair.c @@ -1392,7 +1392,7 @@ static const struct msm_pingroup blair_groups[] = { [26] = PINGROUP(26, qup13_f1, qup13_f2, NA, NA, NA, NA, NA, NA, NA, 0, -1), [27] = PINGROUP(27, qup11_f1, qup11_f2, MDP_VSYNC, PLL_BIST, NA, qdss_gpio14, NA, NA, NA, 0x9C010, 0), - [28] = PINGROUP(28, qup11_f2, qup11_f2, MDP_VSYNC, NA, qdss_gpio15, NA, NA, + [28] = PINGROUP(28, qup11_f1, qup11_f2, MDP_VSYNC, NA, qdss_gpio15, NA, NA, NA, NA, 0x9C010, 1), [29] = PINGROUP(29, cam_mclk, NA, NA, NA, NA, NA, NA, NA, NA, 0, -1), [30] = PINGROUP(30, cam_mclk, NA, NA, NA, NA, NA, NA, NA, NA, 0, -1), From fddd9b5bd6a28251b891b312f368a0df9c2bdd99 Mon Sep 17 00:00:00 2001 From: Rohith Kollalsi Date: Thu, 6 Jan 2022 14:56:51 +0530 Subject: [PATCH 19/29] usb: u_qdss: Add proper error handling in case of mapping failure In case, if mapping of data_buffer or desc_buffer fails in usb_bam_alloc_buffer, iova of it could be corrupt and there is no error handling present for that failure. So qdss driver thinks that qdss_set_data_connection is successful and proceeds for ep_queue which can lead to usb controller accessing corrupt iova address outside the dma pool of usb. Fix this by adding proper error handling in case of data_buffer or desc_buffer mapping failure. Change-Id: Id15ab1f90be052ca0fac209dc619b2e740e1531a Signed-off-by: Rohith Kollalsi --- drivers/platform/msm/usb_bam.c | 24 ++++++++++++++++++++++-- drivers/usb/gadget/function/u_qdss.c | 11 ++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c index bce3fe6bf168..152d0b4263e9 100644 --- a/drivers/platform/msm/usb_bam.c +++ b/drivers/platform/msm/usb_bam.c @@ -334,9 +334,15 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) memset_io(data_buf->base, 0, data_buf->size); data_buf->iova = dma_map_resource(dev, data_buf->phys_base, data_buf->size, DMA_BIDIRECTIONAL, 0); - if (dma_mapping_error(dev, data_buf->iova)) + if (dma_mapping_error(dev, data_buf->iova)) { log_event_err("%s(): oci_mem: err mapping data_buf\n", __func__); + iounmap(data_buf->base); + data_buf->base = NULL; + ret = -ENOMEM; + goto err_exit; + } + log_event_dbg("%s: data_buf:%s virt:%pK, phys:%lx, iova:%lx\n", __func__, dev_name(dev), data_buf->base, (unsigned long)data_buf->phys_base, data_buf->iova); @@ -348,7 +354,11 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) if (!desc_buf->base) { log_event_err("%s: ioremap failed for desc fifo\n", __func__); + dma_unmap_resource(dev, data_buf->iova, + data_buf->size, + DMA_BIDIRECTIONAL, 0); iounmap(data_buf->base); + data_buf->base = NULL; ret = -ENOMEM; goto err_exit; } @@ -356,9 +366,19 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) desc_buf->iova = dma_map_resource(dev, desc_buf->phys_base, desc_buf->size, DMA_BIDIRECTIONAL, 0); - if (dma_mapping_error(dev, desc_buf->iova)) + if (dma_mapping_error(dev, desc_buf->iova)) { log_event_err("%s(): oci_mem: err mapping desc_buf\n", __func__); + dma_unmap_resource(dev, data_buf->iova, + data_buf->size, + DMA_BIDIRECTIONAL, 0); + iounmap(data_buf->base); + data_buf->base = NULL; + iounmap(desc_buf->base); + desc_buf->base = NULL; + ret = -ENOMEM; + goto err_exit; + } log_event_dbg("%s: desc_buf:%s virt:%pK, phys:%lx, iova:%lx\n", __func__, dev_name(dev), desc_buf->base, diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c index 5556ed2d992c..37cb678e0eef 100644 --- a/drivers/usb/gadget/function/u_qdss.c +++ b/drivers/usb/gadget/function/u_qdss.c @@ -78,7 +78,16 @@ int set_qdss_data_connection(struct f_qdss *qdss, int enable) return -ENOMEM; } - usb_bam_alloc_fifos(usb_bam_type, idx); + ret = usb_bam_alloc_fifos(usb_bam_type, idx); + if (ret) { + pr_err("%s: usb_bam_alloc_fifos failed(%d)\n", + __func__, ret); + dma_unmap_resource(dev->parent, bam_info.qdss_bam_iova, + bam_info.qdss_bam_size, + DMA_BIDIRECTIONAL, 0); + return ret; + } + bam_info.data_fifo = kzalloc(sizeof(struct sps_mem_buffer), GFP_KERNEL); if (!bam_info.data_fifo) { From 2c57ec46e1f9e2003bf97e4051f4447c756ec1a8 Mon Sep 17 00:00:00 2001 From: Yue Liu Date: Mon, 6 Dec 2021 13:35:44 +0800 Subject: [PATCH 20/29] msm_serial: msm_uart_next_id decrease when probe failed When the msm_serial driver defer probed, if msm_uart_next_id increased at the beginning of probe, should decreased when probe failed. Change-Id: I8a2594afca17c4ab5fef78da822ffdf39377d0bb Signed-off-by: Yue Liu --- drivers/tty/serial/msm_serial.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 065bf21d95eb..fefa017ff8a6 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -1778,14 +1778,17 @@ static int msm_serial_probe(struct platform_device *pdev) struct uart_port *port; const struct of_device_id *id; int irq, line; + bool flag = false; if (pdev->dev.of_node) line = of_alias_get_id(pdev->dev.of_node, "serial"); else line = pdev->id; - if (line < 0) + if (line < 0) { + flag = true; line = atomic_inc_return(&msm_uart_next_id) - 1; + } if (unlikely(line < 0 || line >= UART_NR)) return -ENXIO; @@ -1803,26 +1806,40 @@ static int msm_serial_probe(struct platform_device *pdev) msm_port->is_uartdm = 0; msm_port->clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(msm_port->clk)) + if (IS_ERR(msm_port->clk)) { + if (flag) + atomic_dec(&msm_uart_next_id); return PTR_ERR(msm_port->clk); + } if (msm_port->is_uartdm) { msm_port->pclk = devm_clk_get(&pdev->dev, "iface"); - if (IS_ERR(msm_port->pclk)) + if (IS_ERR(msm_port->pclk)) { + if (flag) + atomic_dec(&msm_uart_next_id); return PTR_ERR(msm_port->pclk); + } } port->uartclk = clk_get_rate(msm_port->clk); dev_info(&pdev->dev, "uartclk = %d\n", port->uartclk); resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(!resource)) + if (unlikely(!resource)) { + if (flag) + atomic_dec(&msm_uart_next_id); return -ENXIO; + } + port->mapbase = resource->start; irq = platform_get_irq(pdev, 0); - if (unlikely(irq < 0)) + if (unlikely(irq < 0)) { + if (flag) + atomic_dec(&msm_uart_next_id); return -ENXIO; + } + port->irq = irq; platform_set_drvdata(pdev, port); From 0c110427c7c154ae285a26dcee0f870f48841451 Mon Sep 17 00:00:00 2001 From: Rohith Kollalsi Date: Mon, 18 Oct 2021 23:19:29 +0530 Subject: [PATCH 21/29] usb: pd: Process request message as soon as it is received Below mentioned is the sequence of operations done during a pr swap. During the issue scenario, the sleep time of 26 ms is almost coinciding between src_send_capabilities and snk_select_capabilities. Due to this sink side is not able to receive accept from the source for the message request sent. Hence, it does a hard reset. State changes during working scenario. Source Sink SRC_Startup SNK_Startup SRC_Send_Capabilities sleeps for 26ms then entertains req from snk SNK_Evaluate_Capability SNK_Select_Capability send message request to src expects accept in 26ms or else does hard reset Message request received from snk SRC_Negotiate_Capability Send message accept SNK_Transition_Sink SRC_Ready SNK_READY Below is the issue scenario Source Sink SRC_Startup SNK_Startup SRC_Send_Capabilities sleeps for 26ms then entertains req from snk SNK_Evaluate_Capability SNK_Select_Capability send message request to src expects accept in 26ms or else does hard reset Message request received from snk during this 26ms sleep, hence it could not send accept Perform hard reset as accept not received within 26ms Fix this by processing the request message immediately after it is received. This makes sure that request message sent from another end is processed quickly, so that it doesn't cause other end to send a hard reset. Change-Id: I7ffc0772df9300f6e56bbb4e4ad9aa3f15b10b8a Signed-off-by: Rohith Kollalsi --- drivers/usb/pd/policy_engine.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 2b118f71748a..99f271551ecc 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -3623,8 +3623,10 @@ sm_done: spin_unlock_irqrestore(&pd->rx_lock, flags); /* requeue if there are any new/pending RX messages */ - if (!ret && !pd->sm_queued) + if (!ret) { + usbpd_dbg(&pd->dev, "Requeuing new/pending RX messages\n"); kick_sm(pd, 0); + } if (!pd->sm_queued) pm_relax(&pd->dev); From b96af0f331fd1fa481d01e7a4a5f19a42fae63e9 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Wed, 25 Mar 2020 15:52:30 +0300 Subject: [PATCH 22/29] net: introduce the MACSEC netdev feature This patch introduce a new netdev feature, which will be used by drivers to state they can perform MACsec transformations in hardware. The patchset was gathered by Mark, macsec functinality itself was implemented by Dmitry, Mark and Pavel Belous. Change-Id: I84cc0abf852a5adb25d3f6d3fd100b1921b66165 Signed-off-by: Antoine Tenart Signed-off-by: Mark Starovoytov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller Git-commit: 5908220b2b3d6918f88cd645a39e1dcb84d1c5d9 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [quic_adamodar@quicinc.com: Moved upstream change in net/ethtool/common.c to net/core/ethtool.c] Signed-off-by: Akshaya Damodaran --- include/linux/netdev_features.h | 3 +++ net/core/ethtool.c | 1 + 2 files changed, 4 insertions(+) diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 6a55176b1b44..cebbaf5afd4a 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -83,6 +83,8 @@ enum { NETIF_F_HW_TLS_RECORD_BIT, /* Offload TLS record */ NETIF_F_GRO_FRAGLIST_BIT, /* Fraglist GRO */ + NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */ + /* * Add your fresh new feature above and remember to update * netdev_features_strings[] in net/ethtool/common.c and maybe @@ -154,6 +156,7 @@ enum { #define NETIF_F_HW_TLS_RX __NETIF_F(HW_TLS_RX) #define NETIF_F_GRO_FRAGLIST __NETIF_F(GRO_FRAGLIST) #define NETIF_F_GSO_FRAGLIST __NETIF_F(GSO_FRAGLIST) +#define NETIF_F_HW_MACSEC __NETIF_F(HW_MACSEC) /* Finds the next feature with the highest number of the range of start till 0. */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 155ec9c0f724..cb47ad906ba5 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -112,6 +112,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_HW_TLS_RECORD_BIT] = "tls-hw-record", [NETIF_F_HW_TLS_TX_BIT] = "tls-hw-tx-offload", [NETIF_F_HW_TLS_RX_BIT] = "tls-hw-rx-offload", + [NETIF_F_HW_MACSEC_BIT] = "macsec-hw-offload", }; static const char From dc4956515de781afebbbaa17fb581ae6511e9e90 Mon Sep 17 00:00:00 2001 From: Akshaya Damodaran Date: Wed, 25 Mar 2020 15:52:31 +0300 Subject: [PATCH 23/29] net: add a reference to MACsec ops in net_device This patch adds a reference to MACsec ops to the net_device structure, allowing net device drivers to implement offloading operations for MACsec. Change-Id: Iedaffed5dbf005a89a5b79030d1165f1019dd33f Signed-off-by: Antoine Tenart Signed-off-by: Mark Starovoytov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller Git-commit: 30e9bb8472f4454d0544020574bb03d96ffa0e52 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [quic_adamodar@quicinc.com: Fix trivial merge conflicts] Signed-off-by: Akshaya Damodaran --- include/linux/netdevice.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 41af3dde4f79..a6c13ae981cb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -54,6 +54,8 @@ struct netpoll_info; struct device; struct phy_device; struct dsa_port; +struct macsec_context; +struct macsec_ops; struct sfp_bus; /* 802.11 specific */ @@ -1809,6 +1811,8 @@ enum netdev_priv_flags { * * @wol_enabled: Wake-on-LAN is enabled * + * @macsec_ops: MACsec offloading ops + * * FIXME: cleanup struct net_device such that network protocol info * moves out. */ @@ -2100,6 +2104,11 @@ struct net_device { bool proto_down; unsigned wol_enabled:1; +#if IS_ENABLED(CONFIG_MACSEC) + /* MACsec management functions */ + const struct macsec_ops *macsec_ops; +#endif + ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); From 21d6b60894b0c6cb0635626f26ffca398625f3e6 Mon Sep 17 00:00:00 2001 From: Akshaya Damodaran Date: Wed, 25 Mar 2020 15:52:32 +0300 Subject: [PATCH 24/29] net: macsec: allow to reference a netdev from a MACsec context This patch allows to reference a net_device from a MACsec context. This is needed to allow implementing MACsec operations in net device drivers. Change-Id: I5f6ea8ef00889987bde03ceca88512ead27d936b Signed-off-by: Antoine Tenart Signed-off-by: Mark Starovoytov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller Git-commit: 8fa9137180b2fd8482b671f7e0bd8cf7538cbf59 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Akshaya Damodaran --- include/net/macsec.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/net/macsec.h b/include/net/macsec.h index 45e274e39f08..52874cdfe226 100644 --- a/include/net/macsec.h +++ b/include/net/macsec.h @@ -231,7 +231,10 @@ struct macsec_secy { * struct macsec_context - MACsec context for hardware offloading */ struct macsec_context { - struct phy_device *phydev; + union { + struct net_device *netdev; + struct phy_device *phydev; + }; enum macsec_offload offload; struct macsec_secy *secy; From da2bdbc79742a7caadea3f7c24296aea211a52e4 Mon Sep 17 00:00:00 2001 From: Swathi K Date: Sat, 8 Jan 2022 00:33:20 +0530 Subject: [PATCH 25/29] msm: adsprpc: Handle UAF in fastrpc debugfs read Use lock to protect maps amongst multiple threads to avoid race condition. Change-Id: Ib0c83dd38ea8e5acb54a1478d10b02385c27ba31 Signed-off-by: Swathi K --- drivers/char/adsprpc.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 05cfe33e6987..0fe92ba12568 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -5350,6 +5350,7 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + spin_lock(&me->hlock); hlist_for_each_entry_safe(gmaps, n, &me->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20d|0x%-18llX|0x%-18X|0x%-20lX\n\n", @@ -5357,18 +5358,21 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, (uint32_t)gmaps->size, gmaps->va); } + spin_unlock(&me->hlock); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20s|%-20s|%-20s|%-20s\n", "len", "refs", "raddr", "flags"); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + spin_lock(&me->hlock); hlist_for_each_entry_safe(gmaps, n, &me->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "0x%-18X|%-20d|%-20lu|%-20u\n", (uint32_t)gmaps->len, gmaps->refs, gmaps->raddr, gmaps->flags); } + spin_unlock(&me->hlock); } else { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "\n%s %13s %d\n", "cid", ":", fl->cid); @@ -5410,12 +5414,14 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + mutex_lock(&fl->map_mutex); hlist_for_each_entry_safe(map, n, &fl->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "0x%-20lX|0x%-20llX|0x%-20zu\n\n", map->va, map->phys, map->size); } + mutex_unlock(&fl->map_mutex); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20s|%-20s|%-20s|%-20s\n", "len", "refs", @@ -5424,24 +5430,27 @@ static ssize_t fastrpc_debugfs_read(struct file *filp, char __user *buffer, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + mutex_lock(&fl->map_mutex); hlist_for_each_entry_safe(map, n, &fl->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20zu|%-20d|0x%-20lX|%-20d\n\n", map->len, map->refs, map->raddr, map->uncached); } + mutex_unlock(&fl->map_mutex); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20s|%-20s\n", "secure", "attr"); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%s%s%s%s%s\n", single_line, single_line, single_line, single_line, single_line); + mutex_lock(&fl->map_mutex); hlist_for_each_entry_safe(map, n, &fl->maps, hn) { len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "%-20d|0x%-20lX\n\n", map->secure, map->attr); } - + mutex_unlock(&fl->map_mutex); len += scnprintf(fileinfo + len, DEBUGFS_SIZE - len, "\n======%s %s %s======\n", title, " LIST OF BUFS ", title); From 8d0283a368983c84ab18f922a84e1dc0cefd7eb4 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 10 Jan 2022 11:05:55 +0800 Subject: [PATCH 26/29] input: qcom-hv-haptics: reset FIFO play logic when hBoost is busy Currently, when waiting for hBoost ready times out, error code -EBUSY is returned. In addition to that, reset the FIFO play logic so that the following FIFO requests can still be served. Meanwhile, add 100us before updating HAPTICS_EN for hBoost to have enough time to handle its power transition as per the hardware recommendation. Change-Id: Iebfc98b589bda747e4c2b6fdf6ff78d2f46ece5d Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 99 +++++++++++++++++----------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 337b62f3998c..a9626830d9ee 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -1919,10 +1920,17 @@ static int haptics_toggle_module_enable(struct haptics_chip *chip) { int rc; + /* + * Updating HAPTICS_EN would vote hBoost enable status. Add 100us + * delay before updating HAPTICS_EN for hBoost to have enough time + * to handle its power transition. + */ + usleep_range(100, 101); rc = haptics_module_enable(chip, false); if (rc < 0) return rc; + usleep_range(100, 101); return haptics_module_enable(chip, true); } @@ -2374,6 +2382,46 @@ static u8 get_direct_play_max_amplitude(struct haptics_chip *chip) return (u8)amplitude; } +static int haptics_stop_fifo_play(struct haptics_chip *chip) +{ + int rc; + u8 val; + + if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { + dev_dbg(chip->dev, "FIFO playing is not in progress\n"); + return 0; + } + + rc = haptics_enable_play(chip, false); + if (rc < 0) + return rc; + + /* restore FIFO play rate back to T_LRA */ + rc = haptics_set_fifo_playrate(chip, T_LRA); + if (rc < 0) + return rc; + + haptics_fifo_empty_irq_config(chip, false); + kvfree(chip->custom_effect->fifo->samples); + chip->custom_effect->fifo->samples = NULL; + + atomic_set(&chip->play.fifo_status.is_busy, 0); + + /* + * All other playing modes would use AUTO mode RC + * calibration except FIFO streaming mode, so restore + * back to AUTO RC calibration after FIFO playing. + */ + val = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val); + if (rc < 0) + return rc; + + dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); + return 0; +} + static int haptics_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { @@ -2443,46 +2491,21 @@ static int haptics_upload_effect(struct input_dev *dev, return rc; } - return haptics_wait_hboost_ready(chip); -} - -static int haptics_stop_fifo_play(struct haptics_chip *chip) -{ - int rc; - u8 val; - - if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { - dev_dbg(chip->dev, "FIFO playing is not in progress\n"); - return 0; + rc = haptics_wait_hboost_ready(chip); + if (rc < 0 && chip->play.pattern_src == FIFO) { + /* + * Call haptics_stop_fifo_play(chip) explicitly if hBoost is + * not ready for the FIFO play. This drops current FIFO play + * but it restores the SW back to initial status so that the + * following FIFO play requests can still be served. + */ + dev_dbg(chip->dev, "stop FIFO play explicitly to restore SW status\n"); + mutex_lock(&chip->play.lock); + haptics_stop_fifo_play(chip); + mutex_unlock(&chip->play.lock); + return rc; } - rc = haptics_enable_play(chip, false); - if (rc < 0) - return rc; - - /* restore FIFO play rate back to T_LRA */ - rc = haptics_set_fifo_playrate(chip, T_LRA); - if (rc < 0) - return rc; - - haptics_fifo_empty_irq_config(chip, false); - kvfree(chip->custom_effect->fifo->samples); - chip->custom_effect->fifo->samples = NULL; - - atomic_set(&chip->play.fifo_status.is_busy, 0); - - /* - * All other playing modes would use AUTO mode RC - * calibration except FIFO streaming mode, so restore - * back to AUTO RC calibration after FIFO playing. - */ - val = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; - rc = haptics_masked_write(chip, chip->cfg_addr_base, - HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val); - if (rc < 0) - return rc; - - dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); return 0; } From 954033020494784e3b7776b4c3aab0019ab144ea Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 14 Jan 2022 11:55:39 +0800 Subject: [PATCH 27/29] input: qcom-hv-haptics: update sequence to get status data As per the hardware recommendation, MOD_STATUS_XT need to be programmed before programming MOD_STATUS_SEL to get the correct status data in all cases. Update it. Change-Id: Ie66b12cdc702f32ebfad78a72396ad3ac4d14adf Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index a9626830d9ee..15720cb95c19 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -891,7 +891,12 @@ static int haptics_get_status_data(struct haptics_chip *chip, mod_sel_val[0] = sel & 0xff; mod_sel_val[1] = (sel >> 8) & 0xff; rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_SEL_REG, mod_sel_val, 2); + HAP_CFG_MOD_STATUS_XT_V2_REG, &mod_sel_val[1], 1); + if (rc < 0) + return rc; + + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, mod_sel_val, 1); if (rc < 0) return rc; From 810fcd9e1a5f8bfcde6e945a0c3fe69ce16d60bb Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Mon, 17 Jan 2022 10:17:34 +0800 Subject: [PATCH 28/29] input: qcom-hv-haptics: set Vmax before toggling HAPTICS_EN in FIFO play Currently, HAPTICS_EN bit is toggled whenever a FIFO pattern is played followed by setting Vmax. However, this would issue a SWR_VMAX handshake and then follow a SPMI_VMAX handshake in HW if haptics effect is played over SWR bus in the background. This back to back Vmax handshake would potentially cause the HW not responding and no vibration being played. To address this, set the Vmax before toggling HAPTICS_EN bit to avoid back to back Vmax handshakes when SWR and FIFO are played concurrently. Change-Id: I0b8924ab7f369db446458fe2510a0da01519a978 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 15720cb95c19..19114337368a 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -2077,22 +2077,11 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return -EINVAL; play->effect = effect; - if (play->pattern_src == FIFO) { - /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ - rc = haptics_toggle_module_enable(chip); - if (rc < 0) - return rc; - } - /* Clamp VMAX for different vibration strength */ rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) return rc; - rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); - if (rc < 0) - return rc; - play->pattern_src = play->effect->src; if (play->pattern_src != PATTERN1 && play->pattern_src != PATTERN2 && @@ -2102,6 +2091,17 @@ static int haptics_load_predefined_effect(struct haptics_chip *chip, return -EINVAL; } + if (play->pattern_src == FIFO) { + /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ + rc = haptics_toggle_module_enable(chip); + if (rc < 0) + return rc; + } + + rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); + if (rc < 0) + return rc; + play->brake = play->effect->brake; /* Config brake settings if it's necessary */ if (play->brake) { @@ -2265,12 +2265,12 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, play->effect = chip->custom_effect; play->brake = NULL; play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff; - /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ - rc = haptics_toggle_module_enable(chip); + rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) goto cleanup; - rc = haptics_set_vmax_mv(chip, play->vmax_mv); + /* Toggle HAPTICS_EN for a clear start point of FIFO playing */ + rc = haptics_toggle_module_enable(chip); if (rc < 0) goto cleanup; From d1d4c012204239a7631fb63d61139c8a1ec6222e Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Thu, 27 Jan 2022 15:27:19 +0530 Subject: [PATCH 29/29] msm: kgsl: Print always on counters if fenced write timed out Dump the always on counter when write on fenced register gets timed out. This will help to sync and analyze GMU traces with dmesg. Change-Id: Ie69c22f0cb81d52989112a9d0e4aa4cfbfa12030 Signed-off-by: Pankaj Gupta --- drivers/gpu/msm/adreno.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 4507ad717509..08a54d3d0478 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2002,2007-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -3131,12 +3132,14 @@ int adreno_gmu_fenced_write(struct adreno_device *adreno_dev, unsigned int status, i; const struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev); unsigned int reg_offset = gpudev->reg_offsets[offset]; + u64 ts1, ts2; adreno_writereg(adreno_dev, offset, val); if (!gmu_core_isenabled(KGSL_DEVICE(adreno_dev))) return 0; + ts1 = gpudev->read_alwayson(adreno_dev); for (i = 0; i < GMU_CORE_LONG_WAKEUP_RETRY_LIMIT; i++) { /* * Make sure the previous register write is posted before @@ -3165,10 +3168,10 @@ int adreno_gmu_fenced_write(struct adreno_device *adreno_dev, return 0; if (i == GMU_CORE_LONG_WAKEUP_RETRY_LIMIT) { + ts2 = gpudev->read_alwayson(adreno_dev); dev_err(adreno_dev->dev.dev, - "Timed out waiting %d usecs to write fenced register 0x%x\n", - i * GMU_CORE_WAKEUP_DELAY_US, - reg_offset); + "Timed out waiting %d usecs to write fenced register 0x%x, timestamps: %llx %llx\n", + i * GMU_CORE_WAKEUP_DELAY_US, reg_offset, ts1, ts2); return -ETIMEDOUT; }