aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/fusb302/Platform_Linux/platform.c
blob: 3ea7fb0e3819a2b65ea3e0e2428c258227ad6e0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
/*
 * fusb302 usb phy driver for type-c and PD
 *
 * Copyright (C) 2015, 2016 Fairchild Semiconductor Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Seee the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include <linux/printk.h>                                                       // pr_err, printk, etc
#include <linux/delay.h>
#include <linux/power_supply.h>
#include "../core/PD_Types.h"
#include "fusb30x_global.h"                                                     // Chip structure
#include "platform_helpers.h"                                                   // Implementation details
#include "../core/platform.h"
#include "../core/TypeC_Types.h"

/*******************************************************************************
* Function:        platform_set/get_vbus_lvl_enable
* Input:           VBUS_LVL - requested voltage
*                  Boolean - enable this voltage level
*                  Boolean - turn off other supported voltages
* Return:          Boolean - on or off
* Description:     Provide access to the VBUS control pins.
******************************************************************************/
void platform_set_vbus_lvl_enable(VBUS_LVL level, FSC_BOOL blnEnable, FSC_BOOL blnDisableOthers)
{
    FSC_U32 i;

    // Additional VBUS levels can be added here as needed.
    switch (level)
    {
    case VBUS_LVL_5V:
        // Enable/Disable the 5V Source
        fusb_GPIO_Set_VBus5v(blnEnable == TRUE ? true : false);
        break;
    case VBUS_LVL_12V:
        // Enable/Disable the 12V Source
        fusb_GPIO_Set_VBusOther(blnEnable == TRUE ? true : false);
        break;
    default:
        // Otherwise, do nothing.
        break;
    }

    // Turn off other levels, if requested
    if (blnDisableOthers || ((level == VBUS_LVL_ALL) && (blnEnable == FALSE)))
    {
        i = 0;

        do {
            // Skip the current level
            if( i == level ) continue;

            // Turn off the other level(s)
            platform_set_vbus_lvl_enable( i, FALSE, FALSE );
        } while (++i < VBUS_LVL_COUNT);
    }

    return;
}

FSC_BOOL platform_get_vbus_lvl_enable(VBUS_LVL level)
{
    // Additional VBUS levels can be added here as needed.
    switch (level)
    {
    case VBUS_LVL_5V:
        // Return the state of the 5V VBUS Source.
        return fusb_GPIO_Get_VBus5v() ? TRUE : FALSE;

    case VBUS_LVL_12V:
        // Return the state of the 12V VBUS Source.
        return fusb_GPIO_Get_VBusOther() ? TRUE : FALSE;

    default:
        // Otherwise, return FALSE.
        return FALSE;
    }
}

extern FSC_BOOL VCONN_enabled;
/*******************************************************************************
 * Function:        platform_set_vconn_enable
 * Input:           blnEnable - enable or disable VCONN
 * Return:          Boolean - State of VCONN GPIO
 * Description:     Provide access to the VCONN control pin(s).
 ******************************************************************************/
FSC_BOOL platform_set_vconn_enable(FSC_BOOL blnEnable)
{
    struct fusb30x_chip *chip = fusb30x_GetChip();
    FSC_BOOL ret;

    ret = fusb_Power_Vconn(blnEnable);

    if (ret && VCONN_enabled != blnEnable) {
        chip->vconn = blnEnable ? DUAL_ROLE_PROP_VCONN_SUPPLY_YES : DUAL_ROLE_PROP_VCONN_SUPPLY_NO;
        if (chip->fusb_instance)
            dual_role_instance_changed(chip->fusb_instance);
    }

    return ret;
}

/*******************************************************************************
* Function:        platform_set_vbus_discharge
* Input:           Boolean
* Return:          None
* Description:     Enable/Disable Vbus Discharge Path
******************************************************************************/
void platform_set_vbus_discharge(FSC_BOOL blnEnable)
{
    // TODO - Implement if required for platform
}

/*******************************************************************************
* Function:        platform_get_device_irq_state
* Input:           None
* Return:          Boolean.  TRUE = Interrupt Active
* Description:     Get the state of the INT_N pin.  INT_N is active low.  This
*                  function handles that by returning TRUE if the pin is
*                  pulled low indicating an active interrupt signal.
******************************************************************************/
FSC_BOOL platform_get_device_irq_state(void)
{
    return fusb_GPIO_Get_IntN() ? TRUE : FALSE;
}

/*******************************************************************************
* Function:        platform_i2c_write
* Input:           SlaveAddress - Slave device bus address
*                  RegAddrLength - Register Address Byte Length
*                  DataLength - Length of data to transmit
*                  PacketSize - Maximum size of each transmitted packet
*                  IncSize - Number of bytes to send before incrementing addr
*                  RegisterAddress - Internal register address
*                  Data - Buffer of char data to transmit
* Return:          Error state
* Description:     Write a char buffer to the I2C peripheral.
******************************************************************************/
FSC_BOOL platform_i2c_write(FSC_U8 SlaveAddress,
                        FSC_U8 RegAddrLength,
                        FSC_U8 DataLength,
                        FSC_U8 PacketSize,
                        FSC_U8 IncSize,
                        FSC_U32 RegisterAddress,
                        FSC_U8* Data)
{
    FSC_BOOL ret = FALSE;
    if (Data == NULL)
    {
        pr_err("%s - Error: Write data buffer is NULL!\n", __func__);
        ret = FALSE;
    }
    else if (fusb_I2C_WriteData((FSC_U8)RegisterAddress, DataLength, Data))
    {
        ret = TRUE;
    }
    else  // I2C Write failure
    {
        ret = FALSE;       // Write data block to the device
    }
    return ret;
}

/*******************************************************************************
* Function:        platform_i2c_read
* Input:           SlaveAddress - Slave device bus address
*                  RegAddrLength - Register Address Byte Length
*                  DataLength - Length of data to attempt to read
*                  PacketSize - Maximum size of each received packet
*                  IncSize - Number of bytes to recv before incrementing addr
*                  RegisterAddress - Internal register address
*                  Data - Buffer for received char data
* Return:          Error state.
* Description:     Read char data from the I2C peripheral.
******************************************************************************/
FSC_BOOL platform_i2c_read(FSC_U8 SlaveAddress,
                       FSC_U8 RegAddrLength,
                       FSC_U8 DataLength,
                       FSC_U8 PacketSize,
                       FSC_U8 IncSize,
                       FSC_U32 RegisterAddress,
                       FSC_U8* Data)
{
    FSC_BOOL ret = FALSE;
    FSC_S32 i = 0;
    FSC_U8 temp = 0;
    struct fusb30x_chip* chip = fusb30x_GetChip();
    if (!chip)
    {
        pr_err("FUSB  %s - Error: Chip structure is NULL!\n", __func__);
        return FALSE;
    }

    if (Data == NULL)
    {
        pr_err("%s - Error: Read data buffer is NULL!\n", __func__);
        ret = FALSE;
    }
    else if (DataLength > 1 && chip->use_i2c_blocks)    // Do block reads if able and necessary
    {
        if (!fusb_I2C_ReadBlockData(RegisterAddress, DataLength, Data))
        {
            ret = FALSE;
        }
        else
        {
            ret = TRUE;
        }
    }
    else
    {
        for (i = 0; i < DataLength; i++)
        {
            if (fusb_I2C_ReadData((FSC_U8)RegisterAddress + i, &temp))
            {
                Data[i] = temp;
                ret = TRUE;
            }
            else
            {
                ret = FALSE;
                break;
            }
        }
    }

    return ret;
}

/*****************************************************************************
* Function:        platform_enable_timer
* Input:           enable - TRUE to enable platform timer, FALSE to disable
* Return:          None
* Description:     Enables or disables platform timer
******************************************************************************/
void platform_enable_timer(FSC_BOOL enable)
{
    if (enable == TRUE)
    {
        fusb_StartTimers();
    }
    else
    {
        fusb_StopTimers();
    }
}

/*****************************************************************************
* Function:        platform_delay_10us
* Input:           delayCount - Number of 10us delays to wait
* Return:          None
* Description:     Perform a software delay in intervals of 10us.
******************************************************************************/
void platform_delay_10us(FSC_U32 delayCount)
{
    fusb_Delay10us(delayCount);
}

extern FSC_BOOL IsPRSwap;
extern CCTermType CC1TermCCDebounce;
extern CCTermType CC2TermCCDebounce;
/*******************************************************************************
* Function:        platform_notify_cc_orientation
* Input:           orientation - Orientation of CC (NONE, CC1, CC2)
* Return:          None
* Description:     A callback used by the core to report to the platform the
*                  current CC orientation. Called in SetStateAttached... and
*                  SetStateUnattached functions.
******************************************************************************/
void platform_notify_cc_orientation(CC_ORIENTATION orientation)
{
	struct pinctrl_state *set_state;
	struct fusb30x_chip* chip = fusb30x_GetChip();

	// Optional: Notify platform of CC orientation
	pr_info("FUSB %s: orientation=[%d], CC1=[%d], CC2=[%d]\n", __func__, orientation, CC1TermCCDebounce, CC2TermCCDebounce);
	if (orientation == CC1) {
		if (chip->fusb302_pinctrl) {
			set_state = pinctrl_lookup_state(chip->fusb302_pinctrl, "usb3_switch_sel_0");
			if (IS_ERR(set_state)) {
				pr_err("cannot get fusb302 pinctrl usb3_switch_sel_0 state");
				return;
			}

			pinctrl_select_state(chip->fusb302_pinctrl, set_state);
		}
	} else if (orientation == CC2) {
		if (chip->fusb302_pinctrl) {
			set_state = pinctrl_lookup_state(chip->fusb302_pinctrl, "usb3_switch_sel_1");
			if (IS_ERR(set_state)) {
				pr_err("cannot get fusb302 pinctrl usb3_switch_sel_1 state");
				return;
			}

			pinctrl_select_state(chip->fusb302_pinctrl, set_state);
		}
	} else if (orientation == NONE) {
		if (chip && chip->uc && chip->uc->pd_vbus_ctrl) {
			chip->uc->pd_vbus_ctrl(-1, FALSE);
			if (!IsPRSwap)
				platform_notify_attached_source(0, false);
		}
	}
}

extern ConnectionState         ConnState;          // Variable indicating the current connection state
extern PolicyState_t           PolicyState;
/*******************************************************************************
* Function:        platform_notify_pd_contract
* Input:           contract - TRUE: Contract, FALSE: No Contract
* Return:          None
* Description:     A callback used by the core to report to the platform the
*                  current PD contract status. Called in PDPolicy.
*******************************************************************************/
void platform_notify_pd_contract(FSC_BOOL contract)
{
	struct fusb30x_chip* chip = fusb30x_GetChip();

    // Optional: Notify platform of PD contract
	pr_info("FUSB %s: Contract=[%d], typec_state(0x%x), pd_state(0x%x)\n", __func__, contract, ConnState, PolicyState);
	if (contract) {
		if (ConnState == AttachedSink) {
			if (chip && chip->uc && chip->uc->pd_vbus_ctrl) {
				chip->uc->pd_vbus_ctrl(0, FALSE);
			}
		} else if (ConnState == AttachedSource) {
			if (chip && chip->uc && chip->uc->pd_vbus_ctrl) {
				chip->uc->pd_vbus_ctrl(1, FALSE);
			}
		}
	}
}

/*******************************************************************************
* Function:        platform_notify_unsupported_accessory
* Input:           None
* Return:          None
* Description:     A callback used by the core to report entry to the
*                  Unsupported Accessory state. The platform may implement
*                  USB Billboard.
*******************************************************************************/
void platform_notify_unsupported_accessory(void)
{
    // Optional: Implement USB Billboard
    printk(KERN_INFO "FUSB %s: invoked\n", __func__);
}

extern FSC_BOOL PolicyIsDFP;
void platform_notify_attached_source(int value, bool connected)
{
	struct fusb30x_chip* chip = fusb30x_GetChip();
	int notify_retry_count = 0;

	pr_info("FUSB %s: value(%d), typec_state(0x%x), pd_state(0x%x)\n", __func__, value, ConnState, PolicyState);

	do {
		if (chip != NULL && chip->uc != NULL && chip->uc->notify_attached_source != NULL) {
			chip->uc->notify_attached_source(chip->uc, value);
			break;
		} else {
			printk(KERN_INFO "FUSB %s: structure null, retry_count = %d\n", __func__, notify_retry_count);
			notify_retry_count++;
			msleep(5000);
		}
	} while (notify_retry_count <= 3);

	PolicyIsDFP = value ? TRUE : FALSE;
	if (connected) {
		chip->pmode = value ? DUAL_ROLE_PROP_MODE_DFP : DUAL_ROLE_PROP_MODE_UFP;
		chip->drole = value ? DUAL_ROLE_PROP_DR_HOST : DUAL_ROLE_PROP_DR_DEVICE;
	} else {
		chip->pmode = DUAL_ROLE_PROP_MODE_NONE;
		chip->drole = DUAL_ROLE_PROP_DR_NONE;
		chip->prole = DUAL_ROLE_PROP_PR_NONE;
	}

	if (chip->fusb_instance)
		dual_role_instance_changed(chip->fusb_instance);
}

FSC_S32 platform_select_source_capability(u8 obj_cnt, doDataObject_t pd_data[7], int *device_max_ma)
{
    return fusb_battery_select_source_capability(obj_cnt, pd_data, device_max_ma);
}

int platform_set_sink_current_previous(CCTermType Termination)
{
    struct fusb30x_chip* chip = fusb30x_GetChip();
    USBTypeCCurrent sinkCurrent;
    int ret = -ENODEV;

    switch (Termination)
    {
        case CCTypeRdUSB:                       // If we detect the default...
            sinkCurrent = utccDefault;
            break;
        case CCTypeRd1p5:                       // If we detect 1.5A
            sinkCurrent = utcc1p5A;
            break;
        case CCTypeRd3p0:                       // If we detect 3.0A
            sinkCurrent = utcc3p0A;
            break;
        default:
            sinkCurrent = utccNone;
            break;
    }

    if (!chip->batt_psy)
        chip->batt_psy = power_supply_get_by_name("battery");

#ifdef CONFIG_HTC_BATT
    if (chip->batt_psy && chip->batt_psy->set_property)
        ret = chip->batt_psy->set_property(chip->batt_psy, POWER_SUPPLY_PROP_TYPEC_SINK_CURRENT, (const union power_supply_propval *)&sinkCurrent);
#endif

    return ret;
}

int usb_controller_register(struct device *parent, struct usb_controller *uc)
{
	struct fusb30x_chip* chip = fusb30x_GetChip();
	if (chip == NULL)
		return -ENODEV;

	chip->uc = uc;
	return 0;
}
EXPORT_SYMBOL_GPL(usb_controller_register);

int usb_typec_ctrl_register(struct device *parent, struct usb_typec_ctrl *utc)
{
	struct fusb30x_chip* chip = fusb30x_GetChip();
	if (chip == NULL)
		return -ENODEV;

	chip->utc = utc;
	return 0;
}
EXPORT_SYMBOL_GPL(usb_typec_ctrl_register);