1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2017-2026 Morse Micro 4 */ 5 #include <linux/types.h> 6 #include <linux/mutex.h> 7 #include <linux/workqueue.h> 8 #include "hif.h" 9 #include "skbq.h" 10 #include "mac.h" 11 #include "bus.h" 12 #include "ps.h" 13 14 static void mm81x_ps_wakeup(struct mm81x_ps *mps) 15 { 16 struct mm81x *mors = container_of(mps, struct mm81x, ps); 17 18 if (!mps->enable || !mps->suspended) 19 return; 20 21 mm81x_set_bus_enable(mors, true); 22 mps->suspended = false; 23 } 24 25 static void mm81x_ps_sleep(struct mm81x_ps *mps) 26 { 27 struct mm81x *mors = container_of(mps, struct mm81x, ps); 28 29 if (!mps->enable || mps->suspended) 30 return; 31 32 mps->suspended = true; 33 mm81x_set_bus_enable(mors, false); 34 } 35 36 static void mm81x_ps_evaluate(struct mm81x_ps *mps) 37 { 38 struct mm81x *mors = container_of(mps, struct mm81x, ps); 39 bool needs_wake = false; 40 unsigned long flags_on_entry = 41 (mors->hif.event_flags & 42 ~BIT(MM81X_HIF_EVT_DATA_TRAFFIC_PAUSE_PEND)); 43 44 if (!mps->enable) 45 return; 46 47 needs_wake = (mps->wakers > 0); 48 needs_wake |= (flags_on_entry > 0); 49 needs_wake |= (mm81x_hif_get_tx_buffered_count(mors) > 0); 50 51 if (needs_wake) { 52 mm81x_ps_wakeup(mps); 53 return; 54 } 55 56 mm81x_ps_sleep(mps); 57 } 58 59 static void mm81x_ps_evaluate_work(struct work_struct *work) 60 { 61 struct mm81x_ps *mps = 62 container_of(work, struct mm81x_ps, delayed_eval_work.work); 63 64 if (mps->enable) { 65 mutex_lock(&mps->lock); 66 mm81x_ps_evaluate(mps); 67 mutex_unlock(&mps->lock); 68 } 69 } 70 71 void mm81x_ps_enable(struct mm81x *mors) 72 { 73 struct mm81x_ps *mps = &mors->ps; 74 75 if (mps->enable) { 76 mutex_lock(&mps->lock); 77 if (mps->wakers == 0) { 78 WARN_ON_ONCE(1); 79 } else { 80 mps->wakers--; 81 mm81x_ps_evaluate(mps); 82 } 83 mutex_unlock(&mps->lock); 84 } 85 } 86 87 void mm81x_ps_disable(struct mm81x *mors) 88 { 89 struct mm81x_ps *mps = &mors->ps; 90 91 if (mps->enable) { 92 mutex_lock(&mps->lock); 93 mps->wakers++; 94 mm81x_ps_evaluate(mps); 95 mutex_unlock(&mps->lock); 96 } 97 } 98 99 int mm81x_ps_init(struct mm81x *mors) 100 { 101 struct mm81x_ps *mps = &mors->ps; 102 103 mps->enable = (mors->bus_type == MM81X_BUS_TYPE_USB); 104 mps->suspended = true; 105 mps->wakers = 1; /* we default to being on */ 106 mutex_init(&mps->lock); 107 INIT_DELAYED_WORK(&mps->delayed_eval_work, mm81x_ps_evaluate_work); 108 109 return 0; 110 } 111 112 void mm81x_ps_finish(struct mm81x *mors) 113 { 114 struct mm81x_ps *mps = &mors->ps; 115 116 if (mps->enable) { 117 mps->enable = false; 118 cancel_delayed_work_sync(&mps->delayed_eval_work); 119 } 120 } 121