1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Shared Transport driver |
4 | * HCI-LL module responsible for TI proprietary HCI_LL protocol |
5 | * Copyright (C) 2009-2010 Texas Instruments |
6 | * Author: Pavan Savoy <pavan_savoy@ti.com> |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) "(stll) :" fmt |
10 | #include <linux/skbuff.h> |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/ti_wilink_st.h> |
14 | |
15 | /**********************************************************************/ |
16 | /* internal functions */ |
17 | static void send_ll_cmd(struct st_data_s *st_data, |
18 | unsigned char cmd) |
19 | { |
20 | |
21 | pr_debug("%s: writing %x" , __func__, cmd); |
22 | st_int_write(st_data, &cmd, 1); |
23 | return; |
24 | } |
25 | |
26 | static void ll_device_want_to_sleep(struct st_data_s *st_data) |
27 | { |
28 | struct kim_data_s *kim_data; |
29 | struct ti_st_plat_data *pdata; |
30 | |
31 | pr_debug("%s" , __func__); |
32 | /* sanity check */ |
33 | if (st_data->ll_state != ST_LL_AWAKE) |
34 | pr_err("ERR hcill: ST_LL_GO_TO_SLEEP_IND" |
35 | "in state %ld" , st_data->ll_state); |
36 | |
37 | send_ll_cmd(st_data, LL_SLEEP_ACK); |
38 | /* update state */ |
39 | st_data->ll_state = ST_LL_ASLEEP; |
40 | |
41 | /* communicate to platform about chip asleep */ |
42 | kim_data = st_data->kim_data; |
43 | pdata = kim_data->kim_pdev->dev.platform_data; |
44 | if (pdata->chip_asleep) |
45 | pdata->chip_asleep(NULL); |
46 | } |
47 | |
48 | static void ll_device_want_to_wakeup(struct st_data_s *st_data) |
49 | { |
50 | struct kim_data_s *kim_data; |
51 | struct ti_st_plat_data *pdata; |
52 | |
53 | /* diff actions in diff states */ |
54 | switch (st_data->ll_state) { |
55 | case ST_LL_ASLEEP: |
56 | send_ll_cmd(st_data, LL_WAKE_UP_ACK); /* send wake_ack */ |
57 | break; |
58 | case ST_LL_ASLEEP_TO_AWAKE: |
59 | /* duplicate wake_ind */ |
60 | pr_err("duplicate wake_ind while waiting for Wake ack" ); |
61 | break; |
62 | case ST_LL_AWAKE: |
63 | /* duplicate wake_ind */ |
64 | pr_err("duplicate wake_ind already AWAKE" ); |
65 | break; |
66 | case ST_LL_AWAKE_TO_ASLEEP: |
67 | /* duplicate wake_ind */ |
68 | pr_err("duplicate wake_ind" ); |
69 | break; |
70 | } |
71 | /* update state */ |
72 | st_data->ll_state = ST_LL_AWAKE; |
73 | |
74 | /* communicate to platform about chip wakeup */ |
75 | kim_data = st_data->kim_data; |
76 | pdata = kim_data->kim_pdev->dev.platform_data; |
77 | if (pdata->chip_awake) |
78 | pdata->chip_awake(NULL); |
79 | } |
80 | |
81 | /**********************************************************************/ |
82 | /* functions invoked by ST Core */ |
83 | |
84 | /* called when ST Core wants to |
85 | * enable ST LL */ |
86 | void st_ll_enable(struct st_data_s *ll) |
87 | { |
88 | ll->ll_state = ST_LL_AWAKE; |
89 | } |
90 | |
91 | /* called when ST Core /local module wants to |
92 | * disable ST LL */ |
93 | void st_ll_disable(struct st_data_s *ll) |
94 | { |
95 | ll->ll_state = ST_LL_INVALID; |
96 | } |
97 | |
98 | /* called when ST Core wants to update the state */ |
99 | void st_ll_wakeup(struct st_data_s *ll) |
100 | { |
101 | if (likely(ll->ll_state != ST_LL_AWAKE)) { |
102 | send_ll_cmd(st_data: ll, LL_WAKE_UP_IND); /* WAKE_IND */ |
103 | ll->ll_state = ST_LL_ASLEEP_TO_AWAKE; |
104 | } else { |
105 | /* don't send the duplicate wake_indication */ |
106 | pr_err(" Chip already AWAKE " ); |
107 | } |
108 | } |
109 | |
110 | /* called when ST Core wants the state */ |
111 | unsigned long st_ll_getstate(struct st_data_s *ll) |
112 | { |
113 | pr_debug(" returning state %ld" , ll->ll_state); |
114 | return ll->ll_state; |
115 | } |
116 | |
117 | /* called from ST Core, when a PM related packet arrives */ |
118 | unsigned long st_ll_sleep_state(struct st_data_s *st_data, |
119 | unsigned char cmd) |
120 | { |
121 | switch (cmd) { |
122 | case LL_SLEEP_IND: /* sleep ind */ |
123 | pr_debug("sleep indication recvd" ); |
124 | ll_device_want_to_sleep(st_data); |
125 | break; |
126 | case LL_SLEEP_ACK: /* sleep ack */ |
127 | pr_err("sleep ack rcvd: host shouldn't" ); |
128 | break; |
129 | case LL_WAKE_UP_IND: /* wake ind */ |
130 | pr_debug("wake indication recvd" ); |
131 | ll_device_want_to_wakeup(st_data); |
132 | break; |
133 | case LL_WAKE_UP_ACK: /* wake ack */ |
134 | pr_debug("wake ack rcvd" ); |
135 | st_data->ll_state = ST_LL_AWAKE; |
136 | break; |
137 | default: |
138 | pr_err(" unknown input/state " ); |
139 | return -EINVAL; |
140 | } |
141 | return 0; |
142 | } |
143 | |
144 | /* Called from ST CORE to initialize ST LL */ |
145 | long st_ll_init(struct st_data_s *ll) |
146 | { |
147 | /* set state to invalid */ |
148 | ll->ll_state = ST_LL_INVALID; |
149 | return 0; |
150 | } |
151 | |
152 | /* Called from ST CORE to de-initialize ST LL */ |
153 | long st_ll_deinit(struct st_data_s *ll) |
154 | { |
155 | return 0; |
156 | } |
157 | |