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