xref: /OK3568_Linux_fs/kernel/drivers/input/misc/ims-pcu.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Driver for IMS Passenger Control Unit Devices
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2013 The IMS Company
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/completion.h>
9*4882a593Smuzhiyun #include <linux/device.h>
10*4882a593Smuzhiyun #include <linux/firmware.h>
11*4882a593Smuzhiyun #include <linux/ihex.h>
12*4882a593Smuzhiyun #include <linux/input.h>
13*4882a593Smuzhiyun #include <linux/kernel.h>
14*4882a593Smuzhiyun #include <linux/leds.h>
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun #include <linux/types.h>
18*4882a593Smuzhiyun #include <linux/usb/input.h>
19*4882a593Smuzhiyun #include <linux/usb/cdc.h>
20*4882a593Smuzhiyun #include <asm/unaligned.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define IMS_PCU_KEYMAP_LEN		32
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun struct ims_pcu_buttons {
25*4882a593Smuzhiyun 	struct input_dev *input;
26*4882a593Smuzhiyun 	char name[32];
27*4882a593Smuzhiyun 	char phys[32];
28*4882a593Smuzhiyun 	unsigned short keymap[IMS_PCU_KEYMAP_LEN];
29*4882a593Smuzhiyun };
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun struct ims_pcu_gamepad {
32*4882a593Smuzhiyun 	struct input_dev *input;
33*4882a593Smuzhiyun 	char name[32];
34*4882a593Smuzhiyun 	char phys[32];
35*4882a593Smuzhiyun };
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun struct ims_pcu_backlight {
38*4882a593Smuzhiyun 	struct led_classdev cdev;
39*4882a593Smuzhiyun 	char name[32];
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #define IMS_PCU_PART_NUMBER_LEN		15
43*4882a593Smuzhiyun #define IMS_PCU_SERIAL_NUMBER_LEN	8
44*4882a593Smuzhiyun #define IMS_PCU_DOM_LEN			8
45*4882a593Smuzhiyun #define IMS_PCU_FW_VERSION_LEN		(9 + 1)
46*4882a593Smuzhiyun #define IMS_PCU_BL_VERSION_LEN		(9 + 1)
47*4882a593Smuzhiyun #define IMS_PCU_BL_RESET_REASON_LEN	(2 + 1)
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun #define IMS_PCU_PCU_B_DEVICE_ID		5
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun #define IMS_PCU_BUF_SIZE		128
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun struct ims_pcu {
54*4882a593Smuzhiyun 	struct usb_device *udev;
55*4882a593Smuzhiyun 	struct device *dev; /* control interface's device, used for logging */
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	unsigned int device_no;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	bool bootloader_mode;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	char part_number[IMS_PCU_PART_NUMBER_LEN];
62*4882a593Smuzhiyun 	char serial_number[IMS_PCU_SERIAL_NUMBER_LEN];
63*4882a593Smuzhiyun 	char date_of_manufacturing[IMS_PCU_DOM_LEN];
64*4882a593Smuzhiyun 	char fw_version[IMS_PCU_FW_VERSION_LEN];
65*4882a593Smuzhiyun 	char bl_version[IMS_PCU_BL_VERSION_LEN];
66*4882a593Smuzhiyun 	char reset_reason[IMS_PCU_BL_RESET_REASON_LEN];
67*4882a593Smuzhiyun 	int update_firmware_status;
68*4882a593Smuzhiyun 	u8 device_id;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	u8 ofn_reg_addr;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	struct usb_interface *ctrl_intf;
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	struct usb_endpoint_descriptor *ep_ctrl;
75*4882a593Smuzhiyun 	struct urb *urb_ctrl;
76*4882a593Smuzhiyun 	u8 *urb_ctrl_buf;
77*4882a593Smuzhiyun 	dma_addr_t ctrl_dma;
78*4882a593Smuzhiyun 	size_t max_ctrl_size;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	struct usb_interface *data_intf;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	struct usb_endpoint_descriptor *ep_in;
83*4882a593Smuzhiyun 	struct urb *urb_in;
84*4882a593Smuzhiyun 	u8 *urb_in_buf;
85*4882a593Smuzhiyun 	dma_addr_t read_dma;
86*4882a593Smuzhiyun 	size_t max_in_size;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	struct usb_endpoint_descriptor *ep_out;
89*4882a593Smuzhiyun 	u8 *urb_out_buf;
90*4882a593Smuzhiyun 	size_t max_out_size;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	u8 read_buf[IMS_PCU_BUF_SIZE];
93*4882a593Smuzhiyun 	u8 read_pos;
94*4882a593Smuzhiyun 	u8 check_sum;
95*4882a593Smuzhiyun 	bool have_stx;
96*4882a593Smuzhiyun 	bool have_dle;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	u8 cmd_buf[IMS_PCU_BUF_SIZE];
99*4882a593Smuzhiyun 	u8 ack_id;
100*4882a593Smuzhiyun 	u8 expected_response;
101*4882a593Smuzhiyun 	u8 cmd_buf_len;
102*4882a593Smuzhiyun 	struct completion cmd_done;
103*4882a593Smuzhiyun 	struct mutex cmd_mutex;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	u32 fw_start_addr;
106*4882a593Smuzhiyun 	u32 fw_end_addr;
107*4882a593Smuzhiyun 	struct completion async_firmware_done;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	struct ims_pcu_buttons buttons;
110*4882a593Smuzhiyun 	struct ims_pcu_gamepad *gamepad;
111*4882a593Smuzhiyun 	struct ims_pcu_backlight backlight;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	bool setup_complete; /* Input and LED devices have been created */
114*4882a593Smuzhiyun };
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun /*********************************************************************
118*4882a593Smuzhiyun  *             Buttons Input device support                          *
119*4882a593Smuzhiyun  *********************************************************************/
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun static const unsigned short ims_pcu_keymap_1[] = {
122*4882a593Smuzhiyun 	[1] = KEY_ATTENDANT_OFF,
123*4882a593Smuzhiyun 	[2] = KEY_ATTENDANT_ON,
124*4882a593Smuzhiyun 	[3] = KEY_LIGHTS_TOGGLE,
125*4882a593Smuzhiyun 	[4] = KEY_VOLUMEUP,
126*4882a593Smuzhiyun 	[5] = KEY_VOLUMEDOWN,
127*4882a593Smuzhiyun 	[6] = KEY_INFO,
128*4882a593Smuzhiyun };
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun static const unsigned short ims_pcu_keymap_2[] = {
131*4882a593Smuzhiyun 	[4] = KEY_VOLUMEUP,
132*4882a593Smuzhiyun 	[5] = KEY_VOLUMEDOWN,
133*4882a593Smuzhiyun 	[6] = KEY_INFO,
134*4882a593Smuzhiyun };
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun static const unsigned short ims_pcu_keymap_3[] = {
137*4882a593Smuzhiyun 	[1] = KEY_HOMEPAGE,
138*4882a593Smuzhiyun 	[2] = KEY_ATTENDANT_TOGGLE,
139*4882a593Smuzhiyun 	[3] = KEY_LIGHTS_TOGGLE,
140*4882a593Smuzhiyun 	[4] = KEY_VOLUMEUP,
141*4882a593Smuzhiyun 	[5] = KEY_VOLUMEDOWN,
142*4882a593Smuzhiyun 	[6] = KEY_DISPLAYTOGGLE,
143*4882a593Smuzhiyun 	[18] = KEY_PLAYPAUSE,
144*4882a593Smuzhiyun };
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun static const unsigned short ims_pcu_keymap_4[] = {
147*4882a593Smuzhiyun 	[1] = KEY_ATTENDANT_OFF,
148*4882a593Smuzhiyun 	[2] = KEY_ATTENDANT_ON,
149*4882a593Smuzhiyun 	[3] = KEY_LIGHTS_TOGGLE,
150*4882a593Smuzhiyun 	[4] = KEY_VOLUMEUP,
151*4882a593Smuzhiyun 	[5] = KEY_VOLUMEDOWN,
152*4882a593Smuzhiyun 	[6] = KEY_INFO,
153*4882a593Smuzhiyun 	[18] = KEY_PLAYPAUSE,
154*4882a593Smuzhiyun };
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun static const unsigned short ims_pcu_keymap_5[] = {
157*4882a593Smuzhiyun 	[1] = KEY_ATTENDANT_OFF,
158*4882a593Smuzhiyun 	[2] = KEY_ATTENDANT_ON,
159*4882a593Smuzhiyun 	[3] = KEY_LIGHTS_TOGGLE,
160*4882a593Smuzhiyun };
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun struct ims_pcu_device_info {
163*4882a593Smuzhiyun 	const unsigned short *keymap;
164*4882a593Smuzhiyun 	size_t keymap_len;
165*4882a593Smuzhiyun 	bool has_gamepad;
166*4882a593Smuzhiyun };
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun #define IMS_PCU_DEVINFO(_n, _gamepad)				\
169*4882a593Smuzhiyun 	[_n] = {						\
170*4882a593Smuzhiyun 		.keymap = ims_pcu_keymap_##_n,			\
171*4882a593Smuzhiyun 		.keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n),	\
172*4882a593Smuzhiyun 		.has_gamepad = _gamepad,			\
173*4882a593Smuzhiyun 	}
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun static const struct ims_pcu_device_info ims_pcu_device_info[] = {
176*4882a593Smuzhiyun 	IMS_PCU_DEVINFO(1, true),
177*4882a593Smuzhiyun 	IMS_PCU_DEVINFO(2, true),
178*4882a593Smuzhiyun 	IMS_PCU_DEVINFO(3, true),
179*4882a593Smuzhiyun 	IMS_PCU_DEVINFO(4, true),
180*4882a593Smuzhiyun 	IMS_PCU_DEVINFO(5, false),
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun 
ims_pcu_buttons_report(struct ims_pcu * pcu,u32 data)183*4882a593Smuzhiyun static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun 	struct ims_pcu_buttons *buttons = &pcu->buttons;
186*4882a593Smuzhiyun 	struct input_dev *input = buttons->input;
187*4882a593Smuzhiyun 	int i;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	for (i = 0; i < 32; i++) {
190*4882a593Smuzhiyun 		unsigned short keycode = buttons->keymap[i];
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 		if (keycode != KEY_RESERVED)
193*4882a593Smuzhiyun 			input_report_key(input, keycode, data & (1UL << i));
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	input_sync(input);
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun 
ims_pcu_setup_buttons(struct ims_pcu * pcu,const unsigned short * keymap,size_t keymap_len)199*4882a593Smuzhiyun static int ims_pcu_setup_buttons(struct ims_pcu *pcu,
200*4882a593Smuzhiyun 				 const unsigned short *keymap,
201*4882a593Smuzhiyun 				 size_t keymap_len)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun 	struct ims_pcu_buttons *buttons = &pcu->buttons;
204*4882a593Smuzhiyun 	struct input_dev *input;
205*4882a593Smuzhiyun 	int i;
206*4882a593Smuzhiyun 	int error;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	input = input_allocate_device();
209*4882a593Smuzhiyun 	if (!input) {
210*4882a593Smuzhiyun 		dev_err(pcu->dev,
211*4882a593Smuzhiyun 			"Not enough memory for input input device\n");
212*4882a593Smuzhiyun 		return -ENOMEM;
213*4882a593Smuzhiyun 	}
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	snprintf(buttons->name, sizeof(buttons->name),
216*4882a593Smuzhiyun 		 "IMS PCU#%d Button Interface", pcu->device_no);
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	usb_make_path(pcu->udev, buttons->phys, sizeof(buttons->phys));
219*4882a593Smuzhiyun 	strlcat(buttons->phys, "/input0", sizeof(buttons->phys));
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len);
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	input->name = buttons->name;
224*4882a593Smuzhiyun 	input->phys = buttons->phys;
225*4882a593Smuzhiyun 	usb_to_input_id(pcu->udev, &input->id);
226*4882a593Smuzhiyun 	input->dev.parent = &pcu->ctrl_intf->dev;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	input->keycode = buttons->keymap;
229*4882a593Smuzhiyun 	input->keycodemax = ARRAY_SIZE(buttons->keymap);
230*4882a593Smuzhiyun 	input->keycodesize = sizeof(buttons->keymap[0]);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	__set_bit(EV_KEY, input->evbit);
233*4882a593Smuzhiyun 	for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++)
234*4882a593Smuzhiyun 		__set_bit(buttons->keymap[i], input->keybit);
235*4882a593Smuzhiyun 	__clear_bit(KEY_RESERVED, input->keybit);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	error = input_register_device(input);
238*4882a593Smuzhiyun 	if (error) {
239*4882a593Smuzhiyun 		dev_err(pcu->dev,
240*4882a593Smuzhiyun 			"Failed to register buttons input device: %d\n",
241*4882a593Smuzhiyun 			error);
242*4882a593Smuzhiyun 		input_free_device(input);
243*4882a593Smuzhiyun 		return error;
244*4882a593Smuzhiyun 	}
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	buttons->input = input;
247*4882a593Smuzhiyun 	return 0;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
ims_pcu_destroy_buttons(struct ims_pcu * pcu)250*4882a593Smuzhiyun static void ims_pcu_destroy_buttons(struct ims_pcu *pcu)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	struct ims_pcu_buttons *buttons = &pcu->buttons;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	input_unregister_device(buttons->input);
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun /*********************************************************************
259*4882a593Smuzhiyun  *             Gamepad Input device support                          *
260*4882a593Smuzhiyun  *********************************************************************/
261*4882a593Smuzhiyun 
ims_pcu_gamepad_report(struct ims_pcu * pcu,u32 data)262*4882a593Smuzhiyun static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	struct ims_pcu_gamepad *gamepad = pcu->gamepad;
265*4882a593Smuzhiyun 	struct input_dev *input = gamepad->input;
266*4882a593Smuzhiyun 	int x, y;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	x = !!(data & (1 << 14)) - !!(data & (1 << 13));
269*4882a593Smuzhiyun 	y = !!(data & (1 << 12)) - !!(data & (1 << 11));
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	input_report_abs(input, ABS_X, x);
272*4882a593Smuzhiyun 	input_report_abs(input, ABS_Y, y);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	input_report_key(input, BTN_A, data & (1 << 7));
275*4882a593Smuzhiyun 	input_report_key(input, BTN_B, data & (1 << 8));
276*4882a593Smuzhiyun 	input_report_key(input, BTN_X, data & (1 << 9));
277*4882a593Smuzhiyun 	input_report_key(input, BTN_Y, data & (1 << 10));
278*4882a593Smuzhiyun 	input_report_key(input, BTN_START, data & (1 << 15));
279*4882a593Smuzhiyun 	input_report_key(input, BTN_SELECT, data & (1 << 16));
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	input_sync(input);
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun 
ims_pcu_setup_gamepad(struct ims_pcu * pcu)284*4882a593Smuzhiyun static int ims_pcu_setup_gamepad(struct ims_pcu *pcu)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun 	struct ims_pcu_gamepad *gamepad;
287*4882a593Smuzhiyun 	struct input_dev *input;
288*4882a593Smuzhiyun 	int error;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	gamepad = kzalloc(sizeof(struct ims_pcu_gamepad), GFP_KERNEL);
291*4882a593Smuzhiyun 	input = input_allocate_device();
292*4882a593Smuzhiyun 	if (!gamepad || !input) {
293*4882a593Smuzhiyun 		dev_err(pcu->dev,
294*4882a593Smuzhiyun 			"Not enough memory for gamepad device\n");
295*4882a593Smuzhiyun 		error = -ENOMEM;
296*4882a593Smuzhiyun 		goto err_free_mem;
297*4882a593Smuzhiyun 	}
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	gamepad->input = input;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	snprintf(gamepad->name, sizeof(gamepad->name),
302*4882a593Smuzhiyun 		 "IMS PCU#%d Gamepad Interface", pcu->device_no);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	usb_make_path(pcu->udev, gamepad->phys, sizeof(gamepad->phys));
305*4882a593Smuzhiyun 	strlcat(gamepad->phys, "/input1", sizeof(gamepad->phys));
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	input->name = gamepad->name;
308*4882a593Smuzhiyun 	input->phys = gamepad->phys;
309*4882a593Smuzhiyun 	usb_to_input_id(pcu->udev, &input->id);
310*4882a593Smuzhiyun 	input->dev.parent = &pcu->ctrl_intf->dev;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	__set_bit(EV_KEY, input->evbit);
313*4882a593Smuzhiyun 	__set_bit(BTN_A, input->keybit);
314*4882a593Smuzhiyun 	__set_bit(BTN_B, input->keybit);
315*4882a593Smuzhiyun 	__set_bit(BTN_X, input->keybit);
316*4882a593Smuzhiyun 	__set_bit(BTN_Y, input->keybit);
317*4882a593Smuzhiyun 	__set_bit(BTN_START, input->keybit);
318*4882a593Smuzhiyun 	__set_bit(BTN_SELECT, input->keybit);
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	__set_bit(EV_ABS, input->evbit);
321*4882a593Smuzhiyun 	input_set_abs_params(input, ABS_X, -1, 1, 0, 0);
322*4882a593Smuzhiyun 	input_set_abs_params(input, ABS_Y, -1, 1, 0, 0);
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	error = input_register_device(input);
325*4882a593Smuzhiyun 	if (error) {
326*4882a593Smuzhiyun 		dev_err(pcu->dev,
327*4882a593Smuzhiyun 			"Failed to register gamepad input device: %d\n",
328*4882a593Smuzhiyun 			error);
329*4882a593Smuzhiyun 		goto err_free_mem;
330*4882a593Smuzhiyun 	}
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	pcu->gamepad = gamepad;
333*4882a593Smuzhiyun 	return 0;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun err_free_mem:
336*4882a593Smuzhiyun 	input_free_device(input);
337*4882a593Smuzhiyun 	kfree(gamepad);
338*4882a593Smuzhiyun 	return error;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun 
ims_pcu_destroy_gamepad(struct ims_pcu * pcu)341*4882a593Smuzhiyun static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
342*4882a593Smuzhiyun {
343*4882a593Smuzhiyun 	struct ims_pcu_gamepad *gamepad = pcu->gamepad;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	input_unregister_device(gamepad->input);
346*4882a593Smuzhiyun 	kfree(gamepad);
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun /*********************************************************************
351*4882a593Smuzhiyun  *             PCU Communication protocol handling                   *
352*4882a593Smuzhiyun  *********************************************************************/
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun #define IMS_PCU_PROTOCOL_STX		0x02
355*4882a593Smuzhiyun #define IMS_PCU_PROTOCOL_ETX		0x03
356*4882a593Smuzhiyun #define IMS_PCU_PROTOCOL_DLE		0x10
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun /* PCU commands */
359*4882a593Smuzhiyun #define IMS_PCU_CMD_STATUS		0xa0
360*4882a593Smuzhiyun #define IMS_PCU_CMD_PCU_RESET		0xa1
361*4882a593Smuzhiyun #define IMS_PCU_CMD_RESET_REASON	0xa2
362*4882a593Smuzhiyun #define IMS_PCU_CMD_SEND_BUTTONS	0xa3
363*4882a593Smuzhiyun #define IMS_PCU_CMD_JUMP_TO_BTLDR	0xa4
364*4882a593Smuzhiyun #define IMS_PCU_CMD_GET_INFO		0xa5
365*4882a593Smuzhiyun #define IMS_PCU_CMD_SET_BRIGHTNESS	0xa6
366*4882a593Smuzhiyun #define IMS_PCU_CMD_EEPROM		0xa7
367*4882a593Smuzhiyun #define IMS_PCU_CMD_GET_FW_VERSION	0xa8
368*4882a593Smuzhiyun #define IMS_PCU_CMD_GET_BL_VERSION	0xa9
369*4882a593Smuzhiyun #define IMS_PCU_CMD_SET_INFO		0xab
370*4882a593Smuzhiyun #define IMS_PCU_CMD_GET_BRIGHTNESS	0xac
371*4882a593Smuzhiyun #define IMS_PCU_CMD_GET_DEVICE_ID	0xae
372*4882a593Smuzhiyun #define IMS_PCU_CMD_SPECIAL_INFO	0xb0
373*4882a593Smuzhiyun #define IMS_PCU_CMD_BOOTLOADER		0xb1	/* Pass data to bootloader */
374*4882a593Smuzhiyun #define IMS_PCU_CMD_OFN_SET_CONFIG	0xb3
375*4882a593Smuzhiyun #define IMS_PCU_CMD_OFN_GET_CONFIG	0xb4
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun /* PCU responses */
378*4882a593Smuzhiyun #define IMS_PCU_RSP_STATUS		0xc0
379*4882a593Smuzhiyun #define IMS_PCU_RSP_PCU_RESET		0	/* Originally 0xc1 */
380*4882a593Smuzhiyun #define IMS_PCU_RSP_RESET_REASON	0xc2
381*4882a593Smuzhiyun #define IMS_PCU_RSP_SEND_BUTTONS	0xc3
382*4882a593Smuzhiyun #define IMS_PCU_RSP_JUMP_TO_BTLDR	0	/* Originally 0xc4 */
383*4882a593Smuzhiyun #define IMS_PCU_RSP_GET_INFO		0xc5
384*4882a593Smuzhiyun #define IMS_PCU_RSP_SET_BRIGHTNESS	0xc6
385*4882a593Smuzhiyun #define IMS_PCU_RSP_EEPROM		0xc7
386*4882a593Smuzhiyun #define IMS_PCU_RSP_GET_FW_VERSION	0xc8
387*4882a593Smuzhiyun #define IMS_PCU_RSP_GET_BL_VERSION	0xc9
388*4882a593Smuzhiyun #define IMS_PCU_RSP_SET_INFO		0xcb
389*4882a593Smuzhiyun #define IMS_PCU_RSP_GET_BRIGHTNESS	0xcc
390*4882a593Smuzhiyun #define IMS_PCU_RSP_CMD_INVALID		0xcd
391*4882a593Smuzhiyun #define IMS_PCU_RSP_GET_DEVICE_ID	0xce
392*4882a593Smuzhiyun #define IMS_PCU_RSP_SPECIAL_INFO	0xd0
393*4882a593Smuzhiyun #define IMS_PCU_RSP_BOOTLOADER		0xd1	/* Bootloader response */
394*4882a593Smuzhiyun #define IMS_PCU_RSP_OFN_SET_CONFIG	0xd2
395*4882a593Smuzhiyun #define IMS_PCU_RSP_OFN_GET_CONFIG	0xd3
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun #define IMS_PCU_RSP_EVNT_BUTTONS	0xe0	/* Unsolicited, button state */
399*4882a593Smuzhiyun #define IMS_PCU_GAMEPAD_MASK		0x0001ff80UL	/* Bits 7 through 16 */
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun #define IMS_PCU_MIN_PACKET_LEN		3
403*4882a593Smuzhiyun #define IMS_PCU_DATA_OFFSET		2
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun #define IMS_PCU_CMD_WRITE_TIMEOUT	100 /* msec */
406*4882a593Smuzhiyun #define IMS_PCU_CMD_RESPONSE_TIMEOUT	500 /* msec */
407*4882a593Smuzhiyun 
ims_pcu_report_events(struct ims_pcu * pcu)408*4882a593Smuzhiyun static void ims_pcu_report_events(struct ims_pcu *pcu)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun 	u32 data = get_unaligned_be32(&pcu->read_buf[3]);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK);
413*4882a593Smuzhiyun 	if (pcu->gamepad)
414*4882a593Smuzhiyun 		ims_pcu_gamepad_report(pcu, data);
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun 
ims_pcu_handle_response(struct ims_pcu * pcu)417*4882a593Smuzhiyun static void ims_pcu_handle_response(struct ims_pcu *pcu)
418*4882a593Smuzhiyun {
419*4882a593Smuzhiyun 	switch (pcu->read_buf[0]) {
420*4882a593Smuzhiyun 	case IMS_PCU_RSP_EVNT_BUTTONS:
421*4882a593Smuzhiyun 		if (likely(pcu->setup_complete))
422*4882a593Smuzhiyun 			ims_pcu_report_events(pcu);
423*4882a593Smuzhiyun 		break;
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	default:
426*4882a593Smuzhiyun 		/*
427*4882a593Smuzhiyun 		 * See if we got command completion.
428*4882a593Smuzhiyun 		 * If both the sequence and response code match save
429*4882a593Smuzhiyun 		 * the data and signal completion.
430*4882a593Smuzhiyun 		 */
431*4882a593Smuzhiyun 		if (pcu->read_buf[0] == pcu->expected_response &&
432*4882a593Smuzhiyun 		    pcu->read_buf[1] == pcu->ack_id - 1) {
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 			memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos);
435*4882a593Smuzhiyun 			pcu->cmd_buf_len = pcu->read_pos;
436*4882a593Smuzhiyun 			complete(&pcu->cmd_done);
437*4882a593Smuzhiyun 		}
438*4882a593Smuzhiyun 		break;
439*4882a593Smuzhiyun 	}
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun 
ims_pcu_process_data(struct ims_pcu * pcu,struct urb * urb)442*4882a593Smuzhiyun static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
443*4882a593Smuzhiyun {
444*4882a593Smuzhiyun 	int i;
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	for (i = 0; i < urb->actual_length; i++) {
447*4882a593Smuzhiyun 		u8 data = pcu->urb_in_buf[i];
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 		/* Skip everything until we get Start Xmit */
450*4882a593Smuzhiyun 		if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX)
451*4882a593Smuzhiyun 			continue;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 		if (pcu->have_dle) {
454*4882a593Smuzhiyun 			pcu->have_dle = false;
455*4882a593Smuzhiyun 			pcu->read_buf[pcu->read_pos++] = data;
456*4882a593Smuzhiyun 			pcu->check_sum += data;
457*4882a593Smuzhiyun 			continue;
458*4882a593Smuzhiyun 		}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 		switch (data) {
461*4882a593Smuzhiyun 		case IMS_PCU_PROTOCOL_STX:
462*4882a593Smuzhiyun 			if (pcu->have_stx)
463*4882a593Smuzhiyun 				dev_warn(pcu->dev,
464*4882a593Smuzhiyun 					 "Unexpected STX at byte %d, discarding old data\n",
465*4882a593Smuzhiyun 					 pcu->read_pos);
466*4882a593Smuzhiyun 			pcu->have_stx = true;
467*4882a593Smuzhiyun 			pcu->have_dle = false;
468*4882a593Smuzhiyun 			pcu->read_pos = 0;
469*4882a593Smuzhiyun 			pcu->check_sum = 0;
470*4882a593Smuzhiyun 			break;
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 		case IMS_PCU_PROTOCOL_DLE:
473*4882a593Smuzhiyun 			pcu->have_dle = true;
474*4882a593Smuzhiyun 			break;
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 		case IMS_PCU_PROTOCOL_ETX:
477*4882a593Smuzhiyun 			if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) {
478*4882a593Smuzhiyun 				dev_warn(pcu->dev,
479*4882a593Smuzhiyun 					 "Short packet received (%d bytes), ignoring\n",
480*4882a593Smuzhiyun 					 pcu->read_pos);
481*4882a593Smuzhiyun 			} else if (pcu->check_sum != 0) {
482*4882a593Smuzhiyun 				dev_warn(pcu->dev,
483*4882a593Smuzhiyun 					 "Invalid checksum in packet (%d bytes), ignoring\n",
484*4882a593Smuzhiyun 					 pcu->read_pos);
485*4882a593Smuzhiyun 			} else {
486*4882a593Smuzhiyun 				ims_pcu_handle_response(pcu);
487*4882a593Smuzhiyun 			}
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 			pcu->have_stx = false;
490*4882a593Smuzhiyun 			pcu->have_dle = false;
491*4882a593Smuzhiyun 			pcu->read_pos = 0;
492*4882a593Smuzhiyun 			break;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 		default:
495*4882a593Smuzhiyun 			pcu->read_buf[pcu->read_pos++] = data;
496*4882a593Smuzhiyun 			pcu->check_sum += data;
497*4882a593Smuzhiyun 			break;
498*4882a593Smuzhiyun 		}
499*4882a593Smuzhiyun 	}
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun 
ims_pcu_byte_needs_escape(u8 byte)502*4882a593Smuzhiyun static bool ims_pcu_byte_needs_escape(u8 byte)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun 	return byte == IMS_PCU_PROTOCOL_STX ||
505*4882a593Smuzhiyun 	       byte == IMS_PCU_PROTOCOL_ETX ||
506*4882a593Smuzhiyun 	       byte == IMS_PCU_PROTOCOL_DLE;
507*4882a593Smuzhiyun }
508*4882a593Smuzhiyun 
ims_pcu_send_cmd_chunk(struct ims_pcu * pcu,u8 command,int chunk,int len)509*4882a593Smuzhiyun static int ims_pcu_send_cmd_chunk(struct ims_pcu *pcu,
510*4882a593Smuzhiyun 				  u8 command, int chunk, int len)
511*4882a593Smuzhiyun {
512*4882a593Smuzhiyun 	int error;
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	error = usb_bulk_msg(pcu->udev,
515*4882a593Smuzhiyun 			     usb_sndbulkpipe(pcu->udev,
516*4882a593Smuzhiyun 					     pcu->ep_out->bEndpointAddress),
517*4882a593Smuzhiyun 			     pcu->urb_out_buf, len,
518*4882a593Smuzhiyun 			     NULL, IMS_PCU_CMD_WRITE_TIMEOUT);
519*4882a593Smuzhiyun 	if (error < 0) {
520*4882a593Smuzhiyun 		dev_dbg(pcu->dev,
521*4882a593Smuzhiyun 			"Sending 0x%02x command failed at chunk %d: %d\n",
522*4882a593Smuzhiyun 			command, chunk, error);
523*4882a593Smuzhiyun 		return error;
524*4882a593Smuzhiyun 	}
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	return 0;
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun 
ims_pcu_send_command(struct ims_pcu * pcu,u8 command,const u8 * data,int len)529*4882a593Smuzhiyun static int ims_pcu_send_command(struct ims_pcu *pcu,
530*4882a593Smuzhiyun 				u8 command, const u8 *data, int len)
531*4882a593Smuzhiyun {
532*4882a593Smuzhiyun 	int count = 0;
533*4882a593Smuzhiyun 	int chunk = 0;
534*4882a593Smuzhiyun 	int delta;
535*4882a593Smuzhiyun 	int i;
536*4882a593Smuzhiyun 	int error;
537*4882a593Smuzhiyun 	u8 csum = 0;
538*4882a593Smuzhiyun 	u8 ack_id;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_STX;
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	/* We know the command need not be escaped */
543*4882a593Smuzhiyun 	pcu->urb_out_buf[count++] = command;
544*4882a593Smuzhiyun 	csum += command;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	ack_id = pcu->ack_id++;
547*4882a593Smuzhiyun 	if (ack_id == 0xff)
548*4882a593Smuzhiyun 		ack_id = pcu->ack_id++;
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	if (ims_pcu_byte_needs_escape(ack_id))
551*4882a593Smuzhiyun 		pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	pcu->urb_out_buf[count++] = ack_id;
554*4882a593Smuzhiyun 	csum += ack_id;
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	for (i = 0; i < len; i++) {
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 		delta = ims_pcu_byte_needs_escape(data[i]) ? 2 : 1;
559*4882a593Smuzhiyun 		if (count + delta >= pcu->max_out_size) {
560*4882a593Smuzhiyun 			error = ims_pcu_send_cmd_chunk(pcu, command,
561*4882a593Smuzhiyun 						       ++chunk, count);
562*4882a593Smuzhiyun 			if (error)
563*4882a593Smuzhiyun 				return error;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 			count = 0;
566*4882a593Smuzhiyun 		}
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 		if (delta == 2)
569*4882a593Smuzhiyun 			pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 		pcu->urb_out_buf[count++] = data[i];
572*4882a593Smuzhiyun 		csum += data[i];
573*4882a593Smuzhiyun 	}
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	csum = 1 + ~csum;
576*4882a593Smuzhiyun 
577*4882a593Smuzhiyun 	delta = ims_pcu_byte_needs_escape(csum) ? 3 : 2;
578*4882a593Smuzhiyun 	if (count + delta >= pcu->max_out_size) {
579*4882a593Smuzhiyun 		error = ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count);
580*4882a593Smuzhiyun 		if (error)
581*4882a593Smuzhiyun 			return error;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 		count = 0;
584*4882a593Smuzhiyun 	}
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	if (delta == 3)
587*4882a593Smuzhiyun 		pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	pcu->urb_out_buf[count++] = csum;
590*4882a593Smuzhiyun 	pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_ETX;
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	return ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count);
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun 
__ims_pcu_execute_command(struct ims_pcu * pcu,u8 command,const void * data,size_t len,u8 expected_response,int response_time)595*4882a593Smuzhiyun static int __ims_pcu_execute_command(struct ims_pcu *pcu,
596*4882a593Smuzhiyun 				     u8 command, const void *data, size_t len,
597*4882a593Smuzhiyun 				     u8 expected_response, int response_time)
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun 	int error;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	pcu->expected_response = expected_response;
602*4882a593Smuzhiyun 	init_completion(&pcu->cmd_done);
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	error = ims_pcu_send_command(pcu, command, data, len);
605*4882a593Smuzhiyun 	if (error)
606*4882a593Smuzhiyun 		return error;
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	if (expected_response &&
609*4882a593Smuzhiyun 	    !wait_for_completion_timeout(&pcu->cmd_done,
610*4882a593Smuzhiyun 					 msecs_to_jiffies(response_time))) {
611*4882a593Smuzhiyun 		dev_dbg(pcu->dev, "Command 0x%02x timed out\n", command);
612*4882a593Smuzhiyun 		return -ETIMEDOUT;
613*4882a593Smuzhiyun 	}
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	return 0;
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun #define ims_pcu_execute_command(pcu, code, data, len)			\
619*4882a593Smuzhiyun 	__ims_pcu_execute_command(pcu,					\
620*4882a593Smuzhiyun 				  IMS_PCU_CMD_##code, data, len,	\
621*4882a593Smuzhiyun 				  IMS_PCU_RSP_##code,			\
622*4882a593Smuzhiyun 				  IMS_PCU_CMD_RESPONSE_TIMEOUT)
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun #define ims_pcu_execute_query(pcu, code)				\
625*4882a593Smuzhiyun 	ims_pcu_execute_command(pcu, code, NULL, 0)
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun /* Bootloader commands */
628*4882a593Smuzhiyun #define IMS_PCU_BL_CMD_QUERY_DEVICE	0xa1
629*4882a593Smuzhiyun #define IMS_PCU_BL_CMD_UNLOCK_CONFIG	0xa2
630*4882a593Smuzhiyun #define IMS_PCU_BL_CMD_ERASE_APP	0xa3
631*4882a593Smuzhiyun #define IMS_PCU_BL_CMD_PROGRAM_DEVICE	0xa4
632*4882a593Smuzhiyun #define IMS_PCU_BL_CMD_PROGRAM_COMPLETE	0xa5
633*4882a593Smuzhiyun #define IMS_PCU_BL_CMD_READ_APP		0xa6
634*4882a593Smuzhiyun #define IMS_PCU_BL_CMD_RESET_DEVICE	0xa7
635*4882a593Smuzhiyun #define IMS_PCU_BL_CMD_LAUNCH_APP	0xa8
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun /* Bootloader commands */
638*4882a593Smuzhiyun #define IMS_PCU_BL_RSP_QUERY_DEVICE	0xc1
639*4882a593Smuzhiyun #define IMS_PCU_BL_RSP_UNLOCK_CONFIG	0xc2
640*4882a593Smuzhiyun #define IMS_PCU_BL_RSP_ERASE_APP	0xc3
641*4882a593Smuzhiyun #define IMS_PCU_BL_RSP_PROGRAM_DEVICE	0xc4
642*4882a593Smuzhiyun #define IMS_PCU_BL_RSP_PROGRAM_COMPLETE	0xc5
643*4882a593Smuzhiyun #define IMS_PCU_BL_RSP_READ_APP		0xc6
644*4882a593Smuzhiyun #define IMS_PCU_BL_RSP_RESET_DEVICE	0	/* originally 0xa7 */
645*4882a593Smuzhiyun #define IMS_PCU_BL_RSP_LAUNCH_APP	0	/* originally 0xa8 */
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun #define IMS_PCU_BL_DATA_OFFSET		3
648*4882a593Smuzhiyun 
__ims_pcu_execute_bl_command(struct ims_pcu * pcu,u8 command,const void * data,size_t len,u8 expected_response,int response_time)649*4882a593Smuzhiyun static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu,
650*4882a593Smuzhiyun 				        u8 command, const void *data, size_t len,
651*4882a593Smuzhiyun 				        u8 expected_response, int response_time)
652*4882a593Smuzhiyun {
653*4882a593Smuzhiyun 	int error;
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	pcu->cmd_buf[0] = command;
656*4882a593Smuzhiyun 	if (data)
657*4882a593Smuzhiyun 		memcpy(&pcu->cmd_buf[1], data, len);
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	error = __ims_pcu_execute_command(pcu,
660*4882a593Smuzhiyun 				IMS_PCU_CMD_BOOTLOADER, pcu->cmd_buf, len + 1,
661*4882a593Smuzhiyun 				expected_response ? IMS_PCU_RSP_BOOTLOADER : 0,
662*4882a593Smuzhiyun 				response_time);
663*4882a593Smuzhiyun 	if (error) {
664*4882a593Smuzhiyun 		dev_err(pcu->dev,
665*4882a593Smuzhiyun 			"Failure when sending 0x%02x command to bootloader, error: %d\n",
666*4882a593Smuzhiyun 			pcu->cmd_buf[0], error);
667*4882a593Smuzhiyun 		return error;
668*4882a593Smuzhiyun 	}
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun 	if (expected_response && pcu->cmd_buf[2] != expected_response) {
671*4882a593Smuzhiyun 		dev_err(pcu->dev,
672*4882a593Smuzhiyun 			"Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n",
673*4882a593Smuzhiyun 			pcu->cmd_buf[2], expected_response);
674*4882a593Smuzhiyun 		return -EINVAL;
675*4882a593Smuzhiyun 	}
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	return 0;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun #define ims_pcu_execute_bl_command(pcu, code, data, len, timeout)	\
681*4882a593Smuzhiyun 	__ims_pcu_execute_bl_command(pcu,				\
682*4882a593Smuzhiyun 				     IMS_PCU_BL_CMD_##code, data, len,	\
683*4882a593Smuzhiyun 				     IMS_PCU_BL_RSP_##code, timeout)	\
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun #define IMS_PCU_INFO_PART_OFFSET	2
686*4882a593Smuzhiyun #define IMS_PCU_INFO_DOM_OFFSET		17
687*4882a593Smuzhiyun #define IMS_PCU_INFO_SERIAL_OFFSET	25
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun #define IMS_PCU_SET_INFO_SIZE		31
690*4882a593Smuzhiyun 
ims_pcu_get_info(struct ims_pcu * pcu)691*4882a593Smuzhiyun static int ims_pcu_get_info(struct ims_pcu *pcu)
692*4882a593Smuzhiyun {
693*4882a593Smuzhiyun 	int error;
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	error = ims_pcu_execute_query(pcu, GET_INFO);
696*4882a593Smuzhiyun 	if (error) {
697*4882a593Smuzhiyun 		dev_err(pcu->dev,
698*4882a593Smuzhiyun 			"GET_INFO command failed, error: %d\n", error);
699*4882a593Smuzhiyun 		return error;
700*4882a593Smuzhiyun 	}
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	memcpy(pcu->part_number,
703*4882a593Smuzhiyun 	       &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET],
704*4882a593Smuzhiyun 	       sizeof(pcu->part_number));
705*4882a593Smuzhiyun 	memcpy(pcu->date_of_manufacturing,
706*4882a593Smuzhiyun 	       &pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET],
707*4882a593Smuzhiyun 	       sizeof(pcu->date_of_manufacturing));
708*4882a593Smuzhiyun 	memcpy(pcu->serial_number,
709*4882a593Smuzhiyun 	       &pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET],
710*4882a593Smuzhiyun 	       sizeof(pcu->serial_number));
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 	return 0;
713*4882a593Smuzhiyun }
714*4882a593Smuzhiyun 
ims_pcu_set_info(struct ims_pcu * pcu)715*4882a593Smuzhiyun static int ims_pcu_set_info(struct ims_pcu *pcu)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun 	int error;
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 	memcpy(&pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET],
720*4882a593Smuzhiyun 	       pcu->part_number, sizeof(pcu->part_number));
721*4882a593Smuzhiyun 	memcpy(&pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET],
722*4882a593Smuzhiyun 	       pcu->date_of_manufacturing, sizeof(pcu->date_of_manufacturing));
723*4882a593Smuzhiyun 	memcpy(&pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET],
724*4882a593Smuzhiyun 	       pcu->serial_number, sizeof(pcu->serial_number));
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	error = ims_pcu_execute_command(pcu, SET_INFO,
727*4882a593Smuzhiyun 					&pcu->cmd_buf[IMS_PCU_DATA_OFFSET],
728*4882a593Smuzhiyun 					IMS_PCU_SET_INFO_SIZE);
729*4882a593Smuzhiyun 	if (error) {
730*4882a593Smuzhiyun 		dev_err(pcu->dev,
731*4882a593Smuzhiyun 			"Failed to update device information, error: %d\n",
732*4882a593Smuzhiyun 			error);
733*4882a593Smuzhiyun 		return error;
734*4882a593Smuzhiyun 	}
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 	return 0;
737*4882a593Smuzhiyun }
738*4882a593Smuzhiyun 
ims_pcu_switch_to_bootloader(struct ims_pcu * pcu)739*4882a593Smuzhiyun static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu)
740*4882a593Smuzhiyun {
741*4882a593Smuzhiyun 	int error;
742*4882a593Smuzhiyun 
743*4882a593Smuzhiyun 	/* Execute jump to the bootoloader */
744*4882a593Smuzhiyun 	error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0);
745*4882a593Smuzhiyun 	if (error) {
746*4882a593Smuzhiyun 		dev_err(pcu->dev,
747*4882a593Smuzhiyun 			"Failure when sending JUMP TO BOOLTLOADER command, error: %d\n",
748*4882a593Smuzhiyun 			error);
749*4882a593Smuzhiyun 		return error;
750*4882a593Smuzhiyun 	}
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 	return 0;
753*4882a593Smuzhiyun }
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun /*********************************************************************
756*4882a593Smuzhiyun  *             Firmware Update handling                              *
757*4882a593Smuzhiyun  *********************************************************************/
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun #define IMS_PCU_FIRMWARE_NAME	"imspcu.fw"
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun struct ims_pcu_flash_fmt {
762*4882a593Smuzhiyun 	__le32 addr;
763*4882a593Smuzhiyun 	u8 len;
764*4882a593Smuzhiyun 	u8 data[];
765*4882a593Smuzhiyun };
766*4882a593Smuzhiyun 
ims_pcu_count_fw_records(const struct firmware * fw)767*4882a593Smuzhiyun static unsigned int ims_pcu_count_fw_records(const struct firmware *fw)
768*4882a593Smuzhiyun {
769*4882a593Smuzhiyun 	const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data;
770*4882a593Smuzhiyun 	unsigned int count = 0;
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun 	while (rec) {
773*4882a593Smuzhiyun 		count++;
774*4882a593Smuzhiyun 		rec = ihex_next_binrec(rec);
775*4882a593Smuzhiyun 	}
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 	return count;
778*4882a593Smuzhiyun }
779*4882a593Smuzhiyun 
ims_pcu_verify_block(struct ims_pcu * pcu,u32 addr,u8 len,const u8 * data)780*4882a593Smuzhiyun static int ims_pcu_verify_block(struct ims_pcu *pcu,
781*4882a593Smuzhiyun 				u32 addr, u8 len, const u8 *data)
782*4882a593Smuzhiyun {
783*4882a593Smuzhiyun 	struct ims_pcu_flash_fmt *fragment;
784*4882a593Smuzhiyun 	int error;
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	fragment = (void *)&pcu->cmd_buf[1];
787*4882a593Smuzhiyun 	put_unaligned_le32(addr, &fragment->addr);
788*4882a593Smuzhiyun 	fragment->len = len;
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 	error = ims_pcu_execute_bl_command(pcu, READ_APP, NULL, 5,
791*4882a593Smuzhiyun 					IMS_PCU_CMD_RESPONSE_TIMEOUT);
792*4882a593Smuzhiyun 	if (error) {
793*4882a593Smuzhiyun 		dev_err(pcu->dev,
794*4882a593Smuzhiyun 			"Failed to retrieve block at 0x%08x, len %d, error: %d\n",
795*4882a593Smuzhiyun 			addr, len, error);
796*4882a593Smuzhiyun 		return error;
797*4882a593Smuzhiyun 	}
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET];
800*4882a593Smuzhiyun 	if (get_unaligned_le32(&fragment->addr) != addr ||
801*4882a593Smuzhiyun 	    fragment->len != len) {
802*4882a593Smuzhiyun 		dev_err(pcu->dev,
803*4882a593Smuzhiyun 			"Wrong block when retrieving 0x%08x (0x%08x), len %d (%d)\n",
804*4882a593Smuzhiyun 			addr, get_unaligned_le32(&fragment->addr),
805*4882a593Smuzhiyun 			len, fragment->len);
806*4882a593Smuzhiyun 		return -EINVAL;
807*4882a593Smuzhiyun 	}
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 	if (memcmp(fragment->data, data, len)) {
810*4882a593Smuzhiyun 		dev_err(pcu->dev,
811*4882a593Smuzhiyun 			"Mismatch in block at 0x%08x, len %d\n",
812*4882a593Smuzhiyun 			addr, len);
813*4882a593Smuzhiyun 		return -EINVAL;
814*4882a593Smuzhiyun 	}
815*4882a593Smuzhiyun 
816*4882a593Smuzhiyun 	return 0;
817*4882a593Smuzhiyun }
818*4882a593Smuzhiyun 
ims_pcu_flash_firmware(struct ims_pcu * pcu,const struct firmware * fw,unsigned int n_fw_records)819*4882a593Smuzhiyun static int ims_pcu_flash_firmware(struct ims_pcu *pcu,
820*4882a593Smuzhiyun 				  const struct firmware *fw,
821*4882a593Smuzhiyun 				  unsigned int n_fw_records)
822*4882a593Smuzhiyun {
823*4882a593Smuzhiyun 	const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data;
824*4882a593Smuzhiyun 	struct ims_pcu_flash_fmt *fragment;
825*4882a593Smuzhiyun 	unsigned int count = 0;
826*4882a593Smuzhiyun 	u32 addr;
827*4882a593Smuzhiyun 	u8 len;
828*4882a593Smuzhiyun 	int error;
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	error = ims_pcu_execute_bl_command(pcu, ERASE_APP, NULL, 0, 2000);
831*4882a593Smuzhiyun 	if (error) {
832*4882a593Smuzhiyun 		dev_err(pcu->dev,
833*4882a593Smuzhiyun 			"Failed to erase application image, error: %d\n",
834*4882a593Smuzhiyun 			error);
835*4882a593Smuzhiyun 		return error;
836*4882a593Smuzhiyun 	}
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 	while (rec) {
839*4882a593Smuzhiyun 		/*
840*4882a593Smuzhiyun 		 * The firmware format is messed up for some reason.
841*4882a593Smuzhiyun 		 * The address twice that of what is needed for some
842*4882a593Smuzhiyun 		 * reason and we end up overwriting half of the data
843*4882a593Smuzhiyun 		 * with the next record.
844*4882a593Smuzhiyun 		 */
845*4882a593Smuzhiyun 		addr = be32_to_cpu(rec->addr) / 2;
846*4882a593Smuzhiyun 		len = be16_to_cpu(rec->len);
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 		fragment = (void *)&pcu->cmd_buf[1];
849*4882a593Smuzhiyun 		put_unaligned_le32(addr, &fragment->addr);
850*4882a593Smuzhiyun 		fragment->len = len;
851*4882a593Smuzhiyun 		memcpy(fragment->data, rec->data, len);
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 		error = ims_pcu_execute_bl_command(pcu, PROGRAM_DEVICE,
854*4882a593Smuzhiyun 						NULL, len + 5,
855*4882a593Smuzhiyun 						IMS_PCU_CMD_RESPONSE_TIMEOUT);
856*4882a593Smuzhiyun 		if (error) {
857*4882a593Smuzhiyun 			dev_err(pcu->dev,
858*4882a593Smuzhiyun 				"Failed to write block at 0x%08x, len %d, error: %d\n",
859*4882a593Smuzhiyun 				addr, len, error);
860*4882a593Smuzhiyun 			return error;
861*4882a593Smuzhiyun 		}
862*4882a593Smuzhiyun 
863*4882a593Smuzhiyun 		if (addr >= pcu->fw_start_addr && addr < pcu->fw_end_addr) {
864*4882a593Smuzhiyun 			error = ims_pcu_verify_block(pcu, addr, len, rec->data);
865*4882a593Smuzhiyun 			if (error)
866*4882a593Smuzhiyun 				return error;
867*4882a593Smuzhiyun 		}
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 		count++;
870*4882a593Smuzhiyun 		pcu->update_firmware_status = (count * 100) / n_fw_records;
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 		rec = ihex_next_binrec(rec);
873*4882a593Smuzhiyun 	}
874*4882a593Smuzhiyun 
875*4882a593Smuzhiyun 	error = ims_pcu_execute_bl_command(pcu, PROGRAM_COMPLETE,
876*4882a593Smuzhiyun 					    NULL, 0, 2000);
877*4882a593Smuzhiyun 	if (error)
878*4882a593Smuzhiyun 		dev_err(pcu->dev,
879*4882a593Smuzhiyun 			"Failed to send PROGRAM_COMPLETE, error: %d\n",
880*4882a593Smuzhiyun 			error);
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 	return 0;
883*4882a593Smuzhiyun }
884*4882a593Smuzhiyun 
ims_pcu_handle_firmware_update(struct ims_pcu * pcu,const struct firmware * fw)885*4882a593Smuzhiyun static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu,
886*4882a593Smuzhiyun 					  const struct firmware *fw)
887*4882a593Smuzhiyun {
888*4882a593Smuzhiyun 	unsigned int n_fw_records;
889*4882a593Smuzhiyun 	int retval;
890*4882a593Smuzhiyun 
891*4882a593Smuzhiyun 	dev_info(pcu->dev, "Updating firmware %s, size: %zu\n",
892*4882a593Smuzhiyun 		 IMS_PCU_FIRMWARE_NAME, fw->size);
893*4882a593Smuzhiyun 
894*4882a593Smuzhiyun 	n_fw_records = ims_pcu_count_fw_records(fw);
895*4882a593Smuzhiyun 
896*4882a593Smuzhiyun 	retval = ims_pcu_flash_firmware(pcu, fw, n_fw_records);
897*4882a593Smuzhiyun 	if (retval)
898*4882a593Smuzhiyun 		goto out;
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun 	retval = ims_pcu_execute_bl_command(pcu, LAUNCH_APP, NULL, 0, 0);
901*4882a593Smuzhiyun 	if (retval)
902*4882a593Smuzhiyun 		dev_err(pcu->dev,
903*4882a593Smuzhiyun 			"Failed to start application image, error: %d\n",
904*4882a593Smuzhiyun 			retval);
905*4882a593Smuzhiyun 
906*4882a593Smuzhiyun out:
907*4882a593Smuzhiyun 	pcu->update_firmware_status = retval;
908*4882a593Smuzhiyun 	sysfs_notify(&pcu->dev->kobj, NULL, "update_firmware_status");
909*4882a593Smuzhiyun 	return retval;
910*4882a593Smuzhiyun }
911*4882a593Smuzhiyun 
ims_pcu_process_async_firmware(const struct firmware * fw,void * context)912*4882a593Smuzhiyun static void ims_pcu_process_async_firmware(const struct firmware *fw,
913*4882a593Smuzhiyun 					   void *context)
914*4882a593Smuzhiyun {
915*4882a593Smuzhiyun 	struct ims_pcu *pcu = context;
916*4882a593Smuzhiyun 	int error;
917*4882a593Smuzhiyun 
918*4882a593Smuzhiyun 	if (!fw) {
919*4882a593Smuzhiyun 		dev_err(pcu->dev, "Failed to get firmware %s\n",
920*4882a593Smuzhiyun 			IMS_PCU_FIRMWARE_NAME);
921*4882a593Smuzhiyun 		goto out;
922*4882a593Smuzhiyun 	}
923*4882a593Smuzhiyun 
924*4882a593Smuzhiyun 	error = ihex_validate_fw(fw);
925*4882a593Smuzhiyun 	if (error) {
926*4882a593Smuzhiyun 		dev_err(pcu->dev, "Firmware %s is invalid\n",
927*4882a593Smuzhiyun 			IMS_PCU_FIRMWARE_NAME);
928*4882a593Smuzhiyun 		goto out;
929*4882a593Smuzhiyun 	}
930*4882a593Smuzhiyun 
931*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
932*4882a593Smuzhiyun 	ims_pcu_handle_firmware_update(pcu, fw);
933*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
934*4882a593Smuzhiyun 
935*4882a593Smuzhiyun 	release_firmware(fw);
936*4882a593Smuzhiyun 
937*4882a593Smuzhiyun out:
938*4882a593Smuzhiyun 	complete(&pcu->async_firmware_done);
939*4882a593Smuzhiyun }
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun /*********************************************************************
942*4882a593Smuzhiyun  *             Backlight LED device support                          *
943*4882a593Smuzhiyun  *********************************************************************/
944*4882a593Smuzhiyun 
945*4882a593Smuzhiyun #define IMS_PCU_MAX_BRIGHTNESS		31998
946*4882a593Smuzhiyun 
ims_pcu_backlight_set_brightness(struct led_classdev * cdev,enum led_brightness value)947*4882a593Smuzhiyun static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
948*4882a593Smuzhiyun 					    enum led_brightness value)
949*4882a593Smuzhiyun {
950*4882a593Smuzhiyun 	struct ims_pcu_backlight *backlight =
951*4882a593Smuzhiyun 			container_of(cdev, struct ims_pcu_backlight, cdev);
952*4882a593Smuzhiyun 	struct ims_pcu *pcu =
953*4882a593Smuzhiyun 			container_of(backlight, struct ims_pcu, backlight);
954*4882a593Smuzhiyun 	__le16 br_val = cpu_to_le16(value);
955*4882a593Smuzhiyun 	int error;
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun 	error = ims_pcu_execute_command(pcu, SET_BRIGHTNESS,
960*4882a593Smuzhiyun 					&br_val, sizeof(br_val));
961*4882a593Smuzhiyun 	if (error && error != -ENODEV)
962*4882a593Smuzhiyun 		dev_warn(pcu->dev,
963*4882a593Smuzhiyun 			 "Failed to set desired brightness %u, error: %d\n",
964*4882a593Smuzhiyun 			 value, error);
965*4882a593Smuzhiyun 
966*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
967*4882a593Smuzhiyun 
968*4882a593Smuzhiyun 	return error;
969*4882a593Smuzhiyun }
970*4882a593Smuzhiyun 
971*4882a593Smuzhiyun static enum led_brightness
ims_pcu_backlight_get_brightness(struct led_classdev * cdev)972*4882a593Smuzhiyun ims_pcu_backlight_get_brightness(struct led_classdev *cdev)
973*4882a593Smuzhiyun {
974*4882a593Smuzhiyun 	struct ims_pcu_backlight *backlight =
975*4882a593Smuzhiyun 			container_of(cdev, struct ims_pcu_backlight, cdev);
976*4882a593Smuzhiyun 	struct ims_pcu *pcu =
977*4882a593Smuzhiyun 			container_of(backlight, struct ims_pcu, backlight);
978*4882a593Smuzhiyun 	int brightness;
979*4882a593Smuzhiyun 	int error;
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	error = ims_pcu_execute_query(pcu, GET_BRIGHTNESS);
984*4882a593Smuzhiyun 	if (error) {
985*4882a593Smuzhiyun 		dev_warn(pcu->dev,
986*4882a593Smuzhiyun 			 "Failed to get current brightness, error: %d\n",
987*4882a593Smuzhiyun 			 error);
988*4882a593Smuzhiyun 		/* Assume the LED is OFF */
989*4882a593Smuzhiyun 		brightness = LED_OFF;
990*4882a593Smuzhiyun 	} else {
991*4882a593Smuzhiyun 		brightness =
992*4882a593Smuzhiyun 			get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
993*4882a593Smuzhiyun 	}
994*4882a593Smuzhiyun 
995*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
996*4882a593Smuzhiyun 
997*4882a593Smuzhiyun 	return brightness;
998*4882a593Smuzhiyun }
999*4882a593Smuzhiyun 
ims_pcu_setup_backlight(struct ims_pcu * pcu)1000*4882a593Smuzhiyun static int ims_pcu_setup_backlight(struct ims_pcu *pcu)
1001*4882a593Smuzhiyun {
1002*4882a593Smuzhiyun 	struct ims_pcu_backlight *backlight = &pcu->backlight;
1003*4882a593Smuzhiyun 	int error;
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun 	snprintf(backlight->name, sizeof(backlight->name),
1006*4882a593Smuzhiyun 		 "pcu%d::kbd_backlight", pcu->device_no);
1007*4882a593Smuzhiyun 
1008*4882a593Smuzhiyun 	backlight->cdev.name = backlight->name;
1009*4882a593Smuzhiyun 	backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS;
1010*4882a593Smuzhiyun 	backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness;
1011*4882a593Smuzhiyun 	backlight->cdev.brightness_set_blocking =
1012*4882a593Smuzhiyun 					 ims_pcu_backlight_set_brightness;
1013*4882a593Smuzhiyun 
1014*4882a593Smuzhiyun 	error = led_classdev_register(pcu->dev, &backlight->cdev);
1015*4882a593Smuzhiyun 	if (error) {
1016*4882a593Smuzhiyun 		dev_err(pcu->dev,
1017*4882a593Smuzhiyun 			"Failed to register backlight LED device, error: %d\n",
1018*4882a593Smuzhiyun 			error);
1019*4882a593Smuzhiyun 		return error;
1020*4882a593Smuzhiyun 	}
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 	return 0;
1023*4882a593Smuzhiyun }
1024*4882a593Smuzhiyun 
ims_pcu_destroy_backlight(struct ims_pcu * pcu)1025*4882a593Smuzhiyun static void ims_pcu_destroy_backlight(struct ims_pcu *pcu)
1026*4882a593Smuzhiyun {
1027*4882a593Smuzhiyun 	struct ims_pcu_backlight *backlight = &pcu->backlight;
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 	led_classdev_unregister(&backlight->cdev);
1030*4882a593Smuzhiyun }
1031*4882a593Smuzhiyun 
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun /*********************************************************************
1034*4882a593Smuzhiyun  *             Sysfs attributes handling                             *
1035*4882a593Smuzhiyun  *********************************************************************/
1036*4882a593Smuzhiyun 
1037*4882a593Smuzhiyun struct ims_pcu_attribute {
1038*4882a593Smuzhiyun 	struct device_attribute dattr;
1039*4882a593Smuzhiyun 	size_t field_offset;
1040*4882a593Smuzhiyun 	int field_length;
1041*4882a593Smuzhiyun };
1042*4882a593Smuzhiyun 
ims_pcu_attribute_show(struct device * dev,struct device_attribute * dattr,char * buf)1043*4882a593Smuzhiyun static ssize_t ims_pcu_attribute_show(struct device *dev,
1044*4882a593Smuzhiyun 				      struct device_attribute *dattr,
1045*4882a593Smuzhiyun 				      char *buf)
1046*4882a593Smuzhiyun {
1047*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1048*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1049*4882a593Smuzhiyun 	struct ims_pcu_attribute *attr =
1050*4882a593Smuzhiyun 			container_of(dattr, struct ims_pcu_attribute, dattr);
1051*4882a593Smuzhiyun 	char *field = (char *)pcu + attr->field_offset;
1052*4882a593Smuzhiyun 
1053*4882a593Smuzhiyun 	return scnprintf(buf, PAGE_SIZE, "%.*s\n", attr->field_length, field);
1054*4882a593Smuzhiyun }
1055*4882a593Smuzhiyun 
ims_pcu_attribute_store(struct device * dev,struct device_attribute * dattr,const char * buf,size_t count)1056*4882a593Smuzhiyun static ssize_t ims_pcu_attribute_store(struct device *dev,
1057*4882a593Smuzhiyun 				       struct device_attribute *dattr,
1058*4882a593Smuzhiyun 				       const char *buf, size_t count)
1059*4882a593Smuzhiyun {
1060*4882a593Smuzhiyun 
1061*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1062*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1063*4882a593Smuzhiyun 	struct ims_pcu_attribute *attr =
1064*4882a593Smuzhiyun 			container_of(dattr, struct ims_pcu_attribute, dattr);
1065*4882a593Smuzhiyun 	char *field = (char *)pcu + attr->field_offset;
1066*4882a593Smuzhiyun 	size_t data_len;
1067*4882a593Smuzhiyun 	int error;
1068*4882a593Smuzhiyun 
1069*4882a593Smuzhiyun 	if (count > attr->field_length)
1070*4882a593Smuzhiyun 		return -EINVAL;
1071*4882a593Smuzhiyun 
1072*4882a593Smuzhiyun 	data_len = strnlen(buf, attr->field_length);
1073*4882a593Smuzhiyun 	if (data_len > attr->field_length)
1074*4882a593Smuzhiyun 		return -EINVAL;
1075*4882a593Smuzhiyun 
1076*4882a593Smuzhiyun 	error = mutex_lock_interruptible(&pcu->cmd_mutex);
1077*4882a593Smuzhiyun 	if (error)
1078*4882a593Smuzhiyun 		return error;
1079*4882a593Smuzhiyun 
1080*4882a593Smuzhiyun 	memset(field, 0, attr->field_length);
1081*4882a593Smuzhiyun 	memcpy(field, buf, data_len);
1082*4882a593Smuzhiyun 
1083*4882a593Smuzhiyun 	error = ims_pcu_set_info(pcu);
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 	/*
1086*4882a593Smuzhiyun 	 * Even if update failed, let's fetch the info again as we just
1087*4882a593Smuzhiyun 	 * clobbered one of the fields.
1088*4882a593Smuzhiyun 	 */
1089*4882a593Smuzhiyun 	ims_pcu_get_info(pcu);
1090*4882a593Smuzhiyun 
1091*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
1092*4882a593Smuzhiyun 
1093*4882a593Smuzhiyun 	return error < 0 ? error : count;
1094*4882a593Smuzhiyun }
1095*4882a593Smuzhiyun 
1096*4882a593Smuzhiyun #define IMS_PCU_ATTR(_field, _mode)					\
1097*4882a593Smuzhiyun struct ims_pcu_attribute ims_pcu_attr_##_field = {			\
1098*4882a593Smuzhiyun 	.dattr = __ATTR(_field, _mode,					\
1099*4882a593Smuzhiyun 			ims_pcu_attribute_show,				\
1100*4882a593Smuzhiyun 			ims_pcu_attribute_store),			\
1101*4882a593Smuzhiyun 	.field_offset = offsetof(struct ims_pcu, _field),		\
1102*4882a593Smuzhiyun 	.field_length = sizeof(((struct ims_pcu *)NULL)->_field),	\
1103*4882a593Smuzhiyun }
1104*4882a593Smuzhiyun 
1105*4882a593Smuzhiyun #define IMS_PCU_RO_ATTR(_field)						\
1106*4882a593Smuzhiyun 		IMS_PCU_ATTR(_field, S_IRUGO)
1107*4882a593Smuzhiyun #define IMS_PCU_RW_ATTR(_field)						\
1108*4882a593Smuzhiyun 		IMS_PCU_ATTR(_field, S_IRUGO | S_IWUSR)
1109*4882a593Smuzhiyun 
1110*4882a593Smuzhiyun static IMS_PCU_RW_ATTR(part_number);
1111*4882a593Smuzhiyun static IMS_PCU_RW_ATTR(serial_number);
1112*4882a593Smuzhiyun static IMS_PCU_RW_ATTR(date_of_manufacturing);
1113*4882a593Smuzhiyun 
1114*4882a593Smuzhiyun static IMS_PCU_RO_ATTR(fw_version);
1115*4882a593Smuzhiyun static IMS_PCU_RO_ATTR(bl_version);
1116*4882a593Smuzhiyun static IMS_PCU_RO_ATTR(reset_reason);
1117*4882a593Smuzhiyun 
ims_pcu_reset_device(struct device * dev,struct device_attribute * dattr,const char * buf,size_t count)1118*4882a593Smuzhiyun static ssize_t ims_pcu_reset_device(struct device *dev,
1119*4882a593Smuzhiyun 				    struct device_attribute *dattr,
1120*4882a593Smuzhiyun 				    const char *buf, size_t count)
1121*4882a593Smuzhiyun {
1122*4882a593Smuzhiyun 	static const u8 reset_byte = 1;
1123*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1124*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1125*4882a593Smuzhiyun 	int value;
1126*4882a593Smuzhiyun 	int error;
1127*4882a593Smuzhiyun 
1128*4882a593Smuzhiyun 	error = kstrtoint(buf, 0, &value);
1129*4882a593Smuzhiyun 	if (error)
1130*4882a593Smuzhiyun 		return error;
1131*4882a593Smuzhiyun 
1132*4882a593Smuzhiyun 	if (value != 1)
1133*4882a593Smuzhiyun 		return -EINVAL;
1134*4882a593Smuzhiyun 
1135*4882a593Smuzhiyun 	dev_info(pcu->dev, "Attempting to reset device\n");
1136*4882a593Smuzhiyun 
1137*4882a593Smuzhiyun 	error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1);
1138*4882a593Smuzhiyun 	if (error) {
1139*4882a593Smuzhiyun 		dev_info(pcu->dev,
1140*4882a593Smuzhiyun 			 "Failed to reset device, error: %d\n",
1141*4882a593Smuzhiyun 			 error);
1142*4882a593Smuzhiyun 		return error;
1143*4882a593Smuzhiyun 	}
1144*4882a593Smuzhiyun 
1145*4882a593Smuzhiyun 	return count;
1146*4882a593Smuzhiyun }
1147*4882a593Smuzhiyun 
1148*4882a593Smuzhiyun static DEVICE_ATTR(reset_device, S_IWUSR, NULL, ims_pcu_reset_device);
1149*4882a593Smuzhiyun 
ims_pcu_update_firmware_store(struct device * dev,struct device_attribute * dattr,const char * buf,size_t count)1150*4882a593Smuzhiyun static ssize_t ims_pcu_update_firmware_store(struct device *dev,
1151*4882a593Smuzhiyun 					     struct device_attribute *dattr,
1152*4882a593Smuzhiyun 					     const char *buf, size_t count)
1153*4882a593Smuzhiyun {
1154*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1155*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1156*4882a593Smuzhiyun 	const struct firmware *fw = NULL;
1157*4882a593Smuzhiyun 	int value;
1158*4882a593Smuzhiyun 	int error;
1159*4882a593Smuzhiyun 
1160*4882a593Smuzhiyun 	error = kstrtoint(buf, 0, &value);
1161*4882a593Smuzhiyun 	if (error)
1162*4882a593Smuzhiyun 		return error;
1163*4882a593Smuzhiyun 
1164*4882a593Smuzhiyun 	if (value != 1)
1165*4882a593Smuzhiyun 		return -EINVAL;
1166*4882a593Smuzhiyun 
1167*4882a593Smuzhiyun 	error = mutex_lock_interruptible(&pcu->cmd_mutex);
1168*4882a593Smuzhiyun 	if (error)
1169*4882a593Smuzhiyun 		return error;
1170*4882a593Smuzhiyun 
1171*4882a593Smuzhiyun 	error = request_ihex_firmware(&fw, IMS_PCU_FIRMWARE_NAME, pcu->dev);
1172*4882a593Smuzhiyun 	if (error) {
1173*4882a593Smuzhiyun 		dev_err(pcu->dev, "Failed to request firmware %s, error: %d\n",
1174*4882a593Smuzhiyun 			IMS_PCU_FIRMWARE_NAME, error);
1175*4882a593Smuzhiyun 		goto out;
1176*4882a593Smuzhiyun 	}
1177*4882a593Smuzhiyun 
1178*4882a593Smuzhiyun 	/*
1179*4882a593Smuzhiyun 	 * If we are already in bootloader mode we can proceed with
1180*4882a593Smuzhiyun 	 * flashing the firmware.
1181*4882a593Smuzhiyun 	 *
1182*4882a593Smuzhiyun 	 * If we are in application mode, then we need to switch into
1183*4882a593Smuzhiyun 	 * bootloader mode, which will cause the device to disconnect
1184*4882a593Smuzhiyun 	 * and reconnect as different device.
1185*4882a593Smuzhiyun 	 */
1186*4882a593Smuzhiyun 	if (pcu->bootloader_mode)
1187*4882a593Smuzhiyun 		error = ims_pcu_handle_firmware_update(pcu, fw);
1188*4882a593Smuzhiyun 	else
1189*4882a593Smuzhiyun 		error = ims_pcu_switch_to_bootloader(pcu);
1190*4882a593Smuzhiyun 
1191*4882a593Smuzhiyun 	release_firmware(fw);
1192*4882a593Smuzhiyun 
1193*4882a593Smuzhiyun out:
1194*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
1195*4882a593Smuzhiyun 	return error ?: count;
1196*4882a593Smuzhiyun }
1197*4882a593Smuzhiyun 
1198*4882a593Smuzhiyun static DEVICE_ATTR(update_firmware, S_IWUSR,
1199*4882a593Smuzhiyun 		   NULL, ims_pcu_update_firmware_store);
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun static ssize_t
ims_pcu_update_firmware_status_show(struct device * dev,struct device_attribute * dattr,char * buf)1202*4882a593Smuzhiyun ims_pcu_update_firmware_status_show(struct device *dev,
1203*4882a593Smuzhiyun 				    struct device_attribute *dattr,
1204*4882a593Smuzhiyun 				    char *buf)
1205*4882a593Smuzhiyun {
1206*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1207*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1208*4882a593Smuzhiyun 
1209*4882a593Smuzhiyun 	return scnprintf(buf, PAGE_SIZE, "%d\n", pcu->update_firmware_status);
1210*4882a593Smuzhiyun }
1211*4882a593Smuzhiyun 
1212*4882a593Smuzhiyun static DEVICE_ATTR(update_firmware_status, S_IRUGO,
1213*4882a593Smuzhiyun 		   ims_pcu_update_firmware_status_show, NULL);
1214*4882a593Smuzhiyun 
1215*4882a593Smuzhiyun static struct attribute *ims_pcu_attrs[] = {
1216*4882a593Smuzhiyun 	&ims_pcu_attr_part_number.dattr.attr,
1217*4882a593Smuzhiyun 	&ims_pcu_attr_serial_number.dattr.attr,
1218*4882a593Smuzhiyun 	&ims_pcu_attr_date_of_manufacturing.dattr.attr,
1219*4882a593Smuzhiyun 	&ims_pcu_attr_fw_version.dattr.attr,
1220*4882a593Smuzhiyun 	&ims_pcu_attr_bl_version.dattr.attr,
1221*4882a593Smuzhiyun 	&ims_pcu_attr_reset_reason.dattr.attr,
1222*4882a593Smuzhiyun 	&dev_attr_reset_device.attr,
1223*4882a593Smuzhiyun 	&dev_attr_update_firmware.attr,
1224*4882a593Smuzhiyun 	&dev_attr_update_firmware_status.attr,
1225*4882a593Smuzhiyun 	NULL
1226*4882a593Smuzhiyun };
1227*4882a593Smuzhiyun 
ims_pcu_is_attr_visible(struct kobject * kobj,struct attribute * attr,int n)1228*4882a593Smuzhiyun static umode_t ims_pcu_is_attr_visible(struct kobject *kobj,
1229*4882a593Smuzhiyun 				       struct attribute *attr, int n)
1230*4882a593Smuzhiyun {
1231*4882a593Smuzhiyun 	struct device *dev = container_of(kobj, struct device, kobj);
1232*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1233*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1234*4882a593Smuzhiyun 	umode_t mode = attr->mode;
1235*4882a593Smuzhiyun 
1236*4882a593Smuzhiyun 	if (pcu->bootloader_mode) {
1237*4882a593Smuzhiyun 		if (attr != &dev_attr_update_firmware_status.attr &&
1238*4882a593Smuzhiyun 		    attr != &dev_attr_update_firmware.attr &&
1239*4882a593Smuzhiyun 		    attr != &dev_attr_reset_device.attr) {
1240*4882a593Smuzhiyun 			mode = 0;
1241*4882a593Smuzhiyun 		}
1242*4882a593Smuzhiyun 	} else {
1243*4882a593Smuzhiyun 		if (attr == &dev_attr_update_firmware_status.attr)
1244*4882a593Smuzhiyun 			mode = 0;
1245*4882a593Smuzhiyun 	}
1246*4882a593Smuzhiyun 
1247*4882a593Smuzhiyun 	return mode;
1248*4882a593Smuzhiyun }
1249*4882a593Smuzhiyun 
1250*4882a593Smuzhiyun static const struct attribute_group ims_pcu_attr_group = {
1251*4882a593Smuzhiyun 	.is_visible	= ims_pcu_is_attr_visible,
1252*4882a593Smuzhiyun 	.attrs		= ims_pcu_attrs,
1253*4882a593Smuzhiyun };
1254*4882a593Smuzhiyun 
1255*4882a593Smuzhiyun /* Support for a separate OFN attribute group */
1256*4882a593Smuzhiyun 
1257*4882a593Smuzhiyun #define OFN_REG_RESULT_OFFSET	2
1258*4882a593Smuzhiyun 
ims_pcu_read_ofn_config(struct ims_pcu * pcu,u8 addr,u8 * data)1259*4882a593Smuzhiyun static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data)
1260*4882a593Smuzhiyun {
1261*4882a593Smuzhiyun 	int error;
1262*4882a593Smuzhiyun 	s16 result;
1263*4882a593Smuzhiyun 
1264*4882a593Smuzhiyun 	error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG,
1265*4882a593Smuzhiyun 					&addr, sizeof(addr));
1266*4882a593Smuzhiyun 	if (error)
1267*4882a593Smuzhiyun 		return error;
1268*4882a593Smuzhiyun 
1269*4882a593Smuzhiyun 	result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
1270*4882a593Smuzhiyun 	if (result < 0)
1271*4882a593Smuzhiyun 		return -EIO;
1272*4882a593Smuzhiyun 
1273*4882a593Smuzhiyun 	/* We only need LSB */
1274*4882a593Smuzhiyun 	*data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET];
1275*4882a593Smuzhiyun 	return 0;
1276*4882a593Smuzhiyun }
1277*4882a593Smuzhiyun 
ims_pcu_write_ofn_config(struct ims_pcu * pcu,u8 addr,u8 data)1278*4882a593Smuzhiyun static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data)
1279*4882a593Smuzhiyun {
1280*4882a593Smuzhiyun 	u8 buffer[] = { addr, data };
1281*4882a593Smuzhiyun 	int error;
1282*4882a593Smuzhiyun 	s16 result;
1283*4882a593Smuzhiyun 
1284*4882a593Smuzhiyun 	error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG,
1285*4882a593Smuzhiyun 					&buffer, sizeof(buffer));
1286*4882a593Smuzhiyun 	if (error)
1287*4882a593Smuzhiyun 		return error;
1288*4882a593Smuzhiyun 
1289*4882a593Smuzhiyun 	result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET);
1290*4882a593Smuzhiyun 	if (result < 0)
1291*4882a593Smuzhiyun 		return -EIO;
1292*4882a593Smuzhiyun 
1293*4882a593Smuzhiyun 	return 0;
1294*4882a593Smuzhiyun }
1295*4882a593Smuzhiyun 
ims_pcu_ofn_reg_data_show(struct device * dev,struct device_attribute * dattr,char * buf)1296*4882a593Smuzhiyun static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev,
1297*4882a593Smuzhiyun 					 struct device_attribute *dattr,
1298*4882a593Smuzhiyun 					 char *buf)
1299*4882a593Smuzhiyun {
1300*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1301*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1302*4882a593Smuzhiyun 	int error;
1303*4882a593Smuzhiyun 	u8 data;
1304*4882a593Smuzhiyun 
1305*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
1306*4882a593Smuzhiyun 	error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data);
1307*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
1308*4882a593Smuzhiyun 
1309*4882a593Smuzhiyun 	if (error)
1310*4882a593Smuzhiyun 		return error;
1311*4882a593Smuzhiyun 
1312*4882a593Smuzhiyun 	return scnprintf(buf, PAGE_SIZE, "%x\n", data);
1313*4882a593Smuzhiyun }
1314*4882a593Smuzhiyun 
ims_pcu_ofn_reg_data_store(struct device * dev,struct device_attribute * dattr,const char * buf,size_t count)1315*4882a593Smuzhiyun static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev,
1316*4882a593Smuzhiyun 					  struct device_attribute *dattr,
1317*4882a593Smuzhiyun 					  const char *buf, size_t count)
1318*4882a593Smuzhiyun {
1319*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1320*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1321*4882a593Smuzhiyun 	int error;
1322*4882a593Smuzhiyun 	u8 value;
1323*4882a593Smuzhiyun 
1324*4882a593Smuzhiyun 	error = kstrtou8(buf, 0, &value);
1325*4882a593Smuzhiyun 	if (error)
1326*4882a593Smuzhiyun 		return error;
1327*4882a593Smuzhiyun 
1328*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
1329*4882a593Smuzhiyun 	error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value);
1330*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
1331*4882a593Smuzhiyun 
1332*4882a593Smuzhiyun 	return error ?: count;
1333*4882a593Smuzhiyun }
1334*4882a593Smuzhiyun 
1335*4882a593Smuzhiyun static DEVICE_ATTR(reg_data, S_IRUGO | S_IWUSR,
1336*4882a593Smuzhiyun 		   ims_pcu_ofn_reg_data_show, ims_pcu_ofn_reg_data_store);
1337*4882a593Smuzhiyun 
ims_pcu_ofn_reg_addr_show(struct device * dev,struct device_attribute * dattr,char * buf)1338*4882a593Smuzhiyun static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev,
1339*4882a593Smuzhiyun 					 struct device_attribute *dattr,
1340*4882a593Smuzhiyun 					 char *buf)
1341*4882a593Smuzhiyun {
1342*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1343*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1344*4882a593Smuzhiyun 	int error;
1345*4882a593Smuzhiyun 
1346*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
1347*4882a593Smuzhiyun 	error = scnprintf(buf, PAGE_SIZE, "%x\n", pcu->ofn_reg_addr);
1348*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
1349*4882a593Smuzhiyun 
1350*4882a593Smuzhiyun 	return error;
1351*4882a593Smuzhiyun }
1352*4882a593Smuzhiyun 
ims_pcu_ofn_reg_addr_store(struct device * dev,struct device_attribute * dattr,const char * buf,size_t count)1353*4882a593Smuzhiyun static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev,
1354*4882a593Smuzhiyun 					  struct device_attribute *dattr,
1355*4882a593Smuzhiyun 					  const char *buf, size_t count)
1356*4882a593Smuzhiyun {
1357*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1358*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1359*4882a593Smuzhiyun 	int error;
1360*4882a593Smuzhiyun 	u8 value;
1361*4882a593Smuzhiyun 
1362*4882a593Smuzhiyun 	error = kstrtou8(buf, 0, &value);
1363*4882a593Smuzhiyun 	if (error)
1364*4882a593Smuzhiyun 		return error;
1365*4882a593Smuzhiyun 
1366*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
1367*4882a593Smuzhiyun 	pcu->ofn_reg_addr = value;
1368*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
1369*4882a593Smuzhiyun 
1370*4882a593Smuzhiyun 	return count;
1371*4882a593Smuzhiyun }
1372*4882a593Smuzhiyun 
1373*4882a593Smuzhiyun static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR,
1374*4882a593Smuzhiyun 		   ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store);
1375*4882a593Smuzhiyun 
1376*4882a593Smuzhiyun struct ims_pcu_ofn_bit_attribute {
1377*4882a593Smuzhiyun 	struct device_attribute dattr;
1378*4882a593Smuzhiyun 	u8 addr;
1379*4882a593Smuzhiyun 	u8 nr;
1380*4882a593Smuzhiyun };
1381*4882a593Smuzhiyun 
ims_pcu_ofn_bit_show(struct device * dev,struct device_attribute * dattr,char * buf)1382*4882a593Smuzhiyun static ssize_t ims_pcu_ofn_bit_show(struct device *dev,
1383*4882a593Smuzhiyun 				    struct device_attribute *dattr,
1384*4882a593Smuzhiyun 				    char *buf)
1385*4882a593Smuzhiyun {
1386*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1387*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1388*4882a593Smuzhiyun 	struct ims_pcu_ofn_bit_attribute *attr =
1389*4882a593Smuzhiyun 		container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
1390*4882a593Smuzhiyun 	int error;
1391*4882a593Smuzhiyun 	u8 data;
1392*4882a593Smuzhiyun 
1393*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
1394*4882a593Smuzhiyun 	error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
1395*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
1396*4882a593Smuzhiyun 
1397*4882a593Smuzhiyun 	if (error)
1398*4882a593Smuzhiyun 		return error;
1399*4882a593Smuzhiyun 
1400*4882a593Smuzhiyun 	return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr)));
1401*4882a593Smuzhiyun }
1402*4882a593Smuzhiyun 
ims_pcu_ofn_bit_store(struct device * dev,struct device_attribute * dattr,const char * buf,size_t count)1403*4882a593Smuzhiyun static ssize_t ims_pcu_ofn_bit_store(struct device *dev,
1404*4882a593Smuzhiyun 				     struct device_attribute *dattr,
1405*4882a593Smuzhiyun 				     const char *buf, size_t count)
1406*4882a593Smuzhiyun {
1407*4882a593Smuzhiyun 	struct usb_interface *intf = to_usb_interface(dev);
1408*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
1409*4882a593Smuzhiyun 	struct ims_pcu_ofn_bit_attribute *attr =
1410*4882a593Smuzhiyun 		container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr);
1411*4882a593Smuzhiyun 	int error;
1412*4882a593Smuzhiyun 	int value;
1413*4882a593Smuzhiyun 	u8 data;
1414*4882a593Smuzhiyun 
1415*4882a593Smuzhiyun 	error = kstrtoint(buf, 0, &value);
1416*4882a593Smuzhiyun 	if (error)
1417*4882a593Smuzhiyun 		return error;
1418*4882a593Smuzhiyun 
1419*4882a593Smuzhiyun 	if (value > 1)
1420*4882a593Smuzhiyun 		return -EINVAL;
1421*4882a593Smuzhiyun 
1422*4882a593Smuzhiyun 	mutex_lock(&pcu->cmd_mutex);
1423*4882a593Smuzhiyun 
1424*4882a593Smuzhiyun 	error = ims_pcu_read_ofn_config(pcu, attr->addr, &data);
1425*4882a593Smuzhiyun 	if (!error) {
1426*4882a593Smuzhiyun 		if (value)
1427*4882a593Smuzhiyun 			data |= 1U << attr->nr;
1428*4882a593Smuzhiyun 		else
1429*4882a593Smuzhiyun 			data &= ~(1U << attr->nr);
1430*4882a593Smuzhiyun 
1431*4882a593Smuzhiyun 		error = ims_pcu_write_ofn_config(pcu, attr->addr, data);
1432*4882a593Smuzhiyun 	}
1433*4882a593Smuzhiyun 
1434*4882a593Smuzhiyun 	mutex_unlock(&pcu->cmd_mutex);
1435*4882a593Smuzhiyun 
1436*4882a593Smuzhiyun 	return error ?: count;
1437*4882a593Smuzhiyun }
1438*4882a593Smuzhiyun 
1439*4882a593Smuzhiyun #define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr)			\
1440*4882a593Smuzhiyun struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = {		\
1441*4882a593Smuzhiyun 	.dattr = __ATTR(_field, S_IWUSR | S_IRUGO,			\
1442*4882a593Smuzhiyun 			ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store),	\
1443*4882a593Smuzhiyun 	.addr = _addr,							\
1444*4882a593Smuzhiyun 	.nr = _nr,							\
1445*4882a593Smuzhiyun }
1446*4882a593Smuzhiyun 
1447*4882a593Smuzhiyun static IMS_PCU_OFN_BIT_ATTR(engine_enable,   0x60, 7);
1448*4882a593Smuzhiyun static IMS_PCU_OFN_BIT_ATTR(speed_enable,    0x60, 6);
1449*4882a593Smuzhiyun static IMS_PCU_OFN_BIT_ATTR(assert_enable,   0x60, 5);
1450*4882a593Smuzhiyun static IMS_PCU_OFN_BIT_ATTR(xyquant_enable,  0x60, 4);
1451*4882a593Smuzhiyun static IMS_PCU_OFN_BIT_ATTR(xyscale_enable,  0x60, 1);
1452*4882a593Smuzhiyun 
1453*4882a593Smuzhiyun static IMS_PCU_OFN_BIT_ATTR(scale_x2,        0x63, 6);
1454*4882a593Smuzhiyun static IMS_PCU_OFN_BIT_ATTR(scale_y2,        0x63, 7);
1455*4882a593Smuzhiyun 
1456*4882a593Smuzhiyun static struct attribute *ims_pcu_ofn_attrs[] = {
1457*4882a593Smuzhiyun 	&dev_attr_reg_data.attr,
1458*4882a593Smuzhiyun 	&dev_attr_reg_addr.attr,
1459*4882a593Smuzhiyun 	&ims_pcu_ofn_attr_engine_enable.dattr.attr,
1460*4882a593Smuzhiyun 	&ims_pcu_ofn_attr_speed_enable.dattr.attr,
1461*4882a593Smuzhiyun 	&ims_pcu_ofn_attr_assert_enable.dattr.attr,
1462*4882a593Smuzhiyun 	&ims_pcu_ofn_attr_xyquant_enable.dattr.attr,
1463*4882a593Smuzhiyun 	&ims_pcu_ofn_attr_xyscale_enable.dattr.attr,
1464*4882a593Smuzhiyun 	&ims_pcu_ofn_attr_scale_x2.dattr.attr,
1465*4882a593Smuzhiyun 	&ims_pcu_ofn_attr_scale_y2.dattr.attr,
1466*4882a593Smuzhiyun 	NULL
1467*4882a593Smuzhiyun };
1468*4882a593Smuzhiyun 
1469*4882a593Smuzhiyun static const struct attribute_group ims_pcu_ofn_attr_group = {
1470*4882a593Smuzhiyun 	.name	= "ofn",
1471*4882a593Smuzhiyun 	.attrs	= ims_pcu_ofn_attrs,
1472*4882a593Smuzhiyun };
1473*4882a593Smuzhiyun 
ims_pcu_irq(struct urb * urb)1474*4882a593Smuzhiyun static void ims_pcu_irq(struct urb *urb)
1475*4882a593Smuzhiyun {
1476*4882a593Smuzhiyun 	struct ims_pcu *pcu = urb->context;
1477*4882a593Smuzhiyun 	int retval, status;
1478*4882a593Smuzhiyun 
1479*4882a593Smuzhiyun 	status = urb->status;
1480*4882a593Smuzhiyun 
1481*4882a593Smuzhiyun 	switch (status) {
1482*4882a593Smuzhiyun 	case 0:
1483*4882a593Smuzhiyun 		/* success */
1484*4882a593Smuzhiyun 		break;
1485*4882a593Smuzhiyun 	case -ECONNRESET:
1486*4882a593Smuzhiyun 	case -ENOENT:
1487*4882a593Smuzhiyun 	case -ESHUTDOWN:
1488*4882a593Smuzhiyun 		/* this urb is terminated, clean up */
1489*4882a593Smuzhiyun 		dev_dbg(pcu->dev, "%s - urb shutting down with status: %d\n",
1490*4882a593Smuzhiyun 			__func__, status);
1491*4882a593Smuzhiyun 		return;
1492*4882a593Smuzhiyun 	default:
1493*4882a593Smuzhiyun 		dev_dbg(pcu->dev, "%s - nonzero urb status received: %d\n",
1494*4882a593Smuzhiyun 			__func__, status);
1495*4882a593Smuzhiyun 		goto exit;
1496*4882a593Smuzhiyun 	}
1497*4882a593Smuzhiyun 
1498*4882a593Smuzhiyun 	dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__,
1499*4882a593Smuzhiyun 		urb->actual_length, urb->actual_length, pcu->urb_in_buf);
1500*4882a593Smuzhiyun 
1501*4882a593Smuzhiyun 	if (urb == pcu->urb_in)
1502*4882a593Smuzhiyun 		ims_pcu_process_data(pcu, urb);
1503*4882a593Smuzhiyun 
1504*4882a593Smuzhiyun exit:
1505*4882a593Smuzhiyun 	retval = usb_submit_urb(urb, GFP_ATOMIC);
1506*4882a593Smuzhiyun 	if (retval && retval != -ENODEV)
1507*4882a593Smuzhiyun 		dev_err(pcu->dev, "%s - usb_submit_urb failed with result %d\n",
1508*4882a593Smuzhiyun 			__func__, retval);
1509*4882a593Smuzhiyun }
1510*4882a593Smuzhiyun 
ims_pcu_buffers_alloc(struct ims_pcu * pcu)1511*4882a593Smuzhiyun static int ims_pcu_buffers_alloc(struct ims_pcu *pcu)
1512*4882a593Smuzhiyun {
1513*4882a593Smuzhiyun 	int error;
1514*4882a593Smuzhiyun 
1515*4882a593Smuzhiyun 	pcu->urb_in_buf = usb_alloc_coherent(pcu->udev, pcu->max_in_size,
1516*4882a593Smuzhiyun 					     GFP_KERNEL, &pcu->read_dma);
1517*4882a593Smuzhiyun 	if (!pcu->urb_in_buf) {
1518*4882a593Smuzhiyun 		dev_err(pcu->dev,
1519*4882a593Smuzhiyun 			"Failed to allocate memory for read buffer\n");
1520*4882a593Smuzhiyun 		return -ENOMEM;
1521*4882a593Smuzhiyun 	}
1522*4882a593Smuzhiyun 
1523*4882a593Smuzhiyun 	pcu->urb_in = usb_alloc_urb(0, GFP_KERNEL);
1524*4882a593Smuzhiyun 	if (!pcu->urb_in) {
1525*4882a593Smuzhiyun 		dev_err(pcu->dev, "Failed to allocate input URB\n");
1526*4882a593Smuzhiyun 		error = -ENOMEM;
1527*4882a593Smuzhiyun 		goto err_free_urb_in_buf;
1528*4882a593Smuzhiyun 	}
1529*4882a593Smuzhiyun 
1530*4882a593Smuzhiyun 	pcu->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1531*4882a593Smuzhiyun 	pcu->urb_in->transfer_dma = pcu->read_dma;
1532*4882a593Smuzhiyun 
1533*4882a593Smuzhiyun 	usb_fill_bulk_urb(pcu->urb_in, pcu->udev,
1534*4882a593Smuzhiyun 			  usb_rcvbulkpipe(pcu->udev,
1535*4882a593Smuzhiyun 					  pcu->ep_in->bEndpointAddress),
1536*4882a593Smuzhiyun 			  pcu->urb_in_buf, pcu->max_in_size,
1537*4882a593Smuzhiyun 			  ims_pcu_irq, pcu);
1538*4882a593Smuzhiyun 
1539*4882a593Smuzhiyun 	/*
1540*4882a593Smuzhiyun 	 * We are using usb_bulk_msg() for sending so there is no point
1541*4882a593Smuzhiyun 	 * in allocating memory with usb_alloc_coherent().
1542*4882a593Smuzhiyun 	 */
1543*4882a593Smuzhiyun 	pcu->urb_out_buf = kmalloc(pcu->max_out_size, GFP_KERNEL);
1544*4882a593Smuzhiyun 	if (!pcu->urb_out_buf) {
1545*4882a593Smuzhiyun 		dev_err(pcu->dev, "Failed to allocate memory for write buffer\n");
1546*4882a593Smuzhiyun 		error = -ENOMEM;
1547*4882a593Smuzhiyun 		goto err_free_in_urb;
1548*4882a593Smuzhiyun 	}
1549*4882a593Smuzhiyun 
1550*4882a593Smuzhiyun 	pcu->urb_ctrl_buf = usb_alloc_coherent(pcu->udev, pcu->max_ctrl_size,
1551*4882a593Smuzhiyun 					       GFP_KERNEL, &pcu->ctrl_dma);
1552*4882a593Smuzhiyun 	if (!pcu->urb_ctrl_buf) {
1553*4882a593Smuzhiyun 		dev_err(pcu->dev,
1554*4882a593Smuzhiyun 			"Failed to allocate memory for read buffer\n");
1555*4882a593Smuzhiyun 		error = -ENOMEM;
1556*4882a593Smuzhiyun 		goto err_free_urb_out_buf;
1557*4882a593Smuzhiyun 	}
1558*4882a593Smuzhiyun 
1559*4882a593Smuzhiyun 	pcu->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL);
1560*4882a593Smuzhiyun 	if (!pcu->urb_ctrl) {
1561*4882a593Smuzhiyun 		dev_err(pcu->dev, "Failed to allocate input URB\n");
1562*4882a593Smuzhiyun 		error = -ENOMEM;
1563*4882a593Smuzhiyun 		goto err_free_urb_ctrl_buf;
1564*4882a593Smuzhiyun 	}
1565*4882a593Smuzhiyun 
1566*4882a593Smuzhiyun 	pcu->urb_ctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1567*4882a593Smuzhiyun 	pcu->urb_ctrl->transfer_dma = pcu->ctrl_dma;
1568*4882a593Smuzhiyun 
1569*4882a593Smuzhiyun 	usb_fill_int_urb(pcu->urb_ctrl, pcu->udev,
1570*4882a593Smuzhiyun 			  usb_rcvintpipe(pcu->udev,
1571*4882a593Smuzhiyun 					 pcu->ep_ctrl->bEndpointAddress),
1572*4882a593Smuzhiyun 			  pcu->urb_ctrl_buf, pcu->max_ctrl_size,
1573*4882a593Smuzhiyun 			  ims_pcu_irq, pcu, pcu->ep_ctrl->bInterval);
1574*4882a593Smuzhiyun 
1575*4882a593Smuzhiyun 	return 0;
1576*4882a593Smuzhiyun 
1577*4882a593Smuzhiyun err_free_urb_ctrl_buf:
1578*4882a593Smuzhiyun 	usb_free_coherent(pcu->udev, pcu->max_ctrl_size,
1579*4882a593Smuzhiyun 			  pcu->urb_ctrl_buf, pcu->ctrl_dma);
1580*4882a593Smuzhiyun err_free_urb_out_buf:
1581*4882a593Smuzhiyun 	kfree(pcu->urb_out_buf);
1582*4882a593Smuzhiyun err_free_in_urb:
1583*4882a593Smuzhiyun 	usb_free_urb(pcu->urb_in);
1584*4882a593Smuzhiyun err_free_urb_in_buf:
1585*4882a593Smuzhiyun 	usb_free_coherent(pcu->udev, pcu->max_in_size,
1586*4882a593Smuzhiyun 			  pcu->urb_in_buf, pcu->read_dma);
1587*4882a593Smuzhiyun 	return error;
1588*4882a593Smuzhiyun }
1589*4882a593Smuzhiyun 
ims_pcu_buffers_free(struct ims_pcu * pcu)1590*4882a593Smuzhiyun static void ims_pcu_buffers_free(struct ims_pcu *pcu)
1591*4882a593Smuzhiyun {
1592*4882a593Smuzhiyun 	usb_kill_urb(pcu->urb_in);
1593*4882a593Smuzhiyun 	usb_free_urb(pcu->urb_in);
1594*4882a593Smuzhiyun 
1595*4882a593Smuzhiyun 	usb_free_coherent(pcu->udev, pcu->max_out_size,
1596*4882a593Smuzhiyun 			  pcu->urb_in_buf, pcu->read_dma);
1597*4882a593Smuzhiyun 
1598*4882a593Smuzhiyun 	kfree(pcu->urb_out_buf);
1599*4882a593Smuzhiyun 
1600*4882a593Smuzhiyun 	usb_kill_urb(pcu->urb_ctrl);
1601*4882a593Smuzhiyun 	usb_free_urb(pcu->urb_ctrl);
1602*4882a593Smuzhiyun 
1603*4882a593Smuzhiyun 	usb_free_coherent(pcu->udev, pcu->max_ctrl_size,
1604*4882a593Smuzhiyun 			  pcu->urb_ctrl_buf, pcu->ctrl_dma);
1605*4882a593Smuzhiyun }
1606*4882a593Smuzhiyun 
1607*4882a593Smuzhiyun static const struct usb_cdc_union_desc *
ims_pcu_get_cdc_union_desc(struct usb_interface * intf)1608*4882a593Smuzhiyun ims_pcu_get_cdc_union_desc(struct usb_interface *intf)
1609*4882a593Smuzhiyun {
1610*4882a593Smuzhiyun 	const void *buf = intf->altsetting->extra;
1611*4882a593Smuzhiyun 	size_t buflen = intf->altsetting->extralen;
1612*4882a593Smuzhiyun 	struct usb_cdc_union_desc *union_desc;
1613*4882a593Smuzhiyun 
1614*4882a593Smuzhiyun 	if (!buf) {
1615*4882a593Smuzhiyun 		dev_err(&intf->dev, "Missing descriptor data\n");
1616*4882a593Smuzhiyun 		return NULL;
1617*4882a593Smuzhiyun 	}
1618*4882a593Smuzhiyun 
1619*4882a593Smuzhiyun 	if (!buflen) {
1620*4882a593Smuzhiyun 		dev_err(&intf->dev, "Zero length descriptor\n");
1621*4882a593Smuzhiyun 		return NULL;
1622*4882a593Smuzhiyun 	}
1623*4882a593Smuzhiyun 
1624*4882a593Smuzhiyun 	while (buflen >= sizeof(*union_desc)) {
1625*4882a593Smuzhiyun 		union_desc = (struct usb_cdc_union_desc *)buf;
1626*4882a593Smuzhiyun 
1627*4882a593Smuzhiyun 		if (union_desc->bLength > buflen) {
1628*4882a593Smuzhiyun 			dev_err(&intf->dev, "Too large descriptor\n");
1629*4882a593Smuzhiyun 			return NULL;
1630*4882a593Smuzhiyun 		}
1631*4882a593Smuzhiyun 
1632*4882a593Smuzhiyun 		if (union_desc->bDescriptorType == USB_DT_CS_INTERFACE &&
1633*4882a593Smuzhiyun 		    union_desc->bDescriptorSubType == USB_CDC_UNION_TYPE) {
1634*4882a593Smuzhiyun 			dev_dbg(&intf->dev, "Found union header\n");
1635*4882a593Smuzhiyun 
1636*4882a593Smuzhiyun 			if (union_desc->bLength >= sizeof(*union_desc))
1637*4882a593Smuzhiyun 				return union_desc;
1638*4882a593Smuzhiyun 
1639*4882a593Smuzhiyun 			dev_err(&intf->dev,
1640*4882a593Smuzhiyun 				"Union descriptor too short (%d vs %zd)\n",
1641*4882a593Smuzhiyun 				union_desc->bLength, sizeof(*union_desc));
1642*4882a593Smuzhiyun 			return NULL;
1643*4882a593Smuzhiyun 		}
1644*4882a593Smuzhiyun 
1645*4882a593Smuzhiyun 		buflen -= union_desc->bLength;
1646*4882a593Smuzhiyun 		buf += union_desc->bLength;
1647*4882a593Smuzhiyun 	}
1648*4882a593Smuzhiyun 
1649*4882a593Smuzhiyun 	dev_err(&intf->dev, "Missing CDC union descriptor\n");
1650*4882a593Smuzhiyun 	return NULL;
1651*4882a593Smuzhiyun }
1652*4882a593Smuzhiyun 
ims_pcu_parse_cdc_data(struct usb_interface * intf,struct ims_pcu * pcu)1653*4882a593Smuzhiyun static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pcu)
1654*4882a593Smuzhiyun {
1655*4882a593Smuzhiyun 	const struct usb_cdc_union_desc *union_desc;
1656*4882a593Smuzhiyun 	struct usb_host_interface *alt;
1657*4882a593Smuzhiyun 
1658*4882a593Smuzhiyun 	union_desc = ims_pcu_get_cdc_union_desc(intf);
1659*4882a593Smuzhiyun 	if (!union_desc)
1660*4882a593Smuzhiyun 		return -EINVAL;
1661*4882a593Smuzhiyun 
1662*4882a593Smuzhiyun 	pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev,
1663*4882a593Smuzhiyun 					 union_desc->bMasterInterface0);
1664*4882a593Smuzhiyun 	if (!pcu->ctrl_intf)
1665*4882a593Smuzhiyun 		return -EINVAL;
1666*4882a593Smuzhiyun 
1667*4882a593Smuzhiyun 	alt = pcu->ctrl_intf->cur_altsetting;
1668*4882a593Smuzhiyun 
1669*4882a593Smuzhiyun 	if (alt->desc.bNumEndpoints < 1)
1670*4882a593Smuzhiyun 		return -ENODEV;
1671*4882a593Smuzhiyun 
1672*4882a593Smuzhiyun 	pcu->ep_ctrl = &alt->endpoint[0].desc;
1673*4882a593Smuzhiyun 	pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl);
1674*4882a593Smuzhiyun 
1675*4882a593Smuzhiyun 	pcu->data_intf = usb_ifnum_to_if(pcu->udev,
1676*4882a593Smuzhiyun 					 union_desc->bSlaveInterface0);
1677*4882a593Smuzhiyun 	if (!pcu->data_intf)
1678*4882a593Smuzhiyun 		return -EINVAL;
1679*4882a593Smuzhiyun 
1680*4882a593Smuzhiyun 	alt = pcu->data_intf->cur_altsetting;
1681*4882a593Smuzhiyun 	if (alt->desc.bNumEndpoints != 2) {
1682*4882a593Smuzhiyun 		dev_err(pcu->dev,
1683*4882a593Smuzhiyun 			"Incorrect number of endpoints on data interface (%d)\n",
1684*4882a593Smuzhiyun 			alt->desc.bNumEndpoints);
1685*4882a593Smuzhiyun 		return -EINVAL;
1686*4882a593Smuzhiyun 	}
1687*4882a593Smuzhiyun 
1688*4882a593Smuzhiyun 	pcu->ep_out = &alt->endpoint[0].desc;
1689*4882a593Smuzhiyun 	if (!usb_endpoint_is_bulk_out(pcu->ep_out)) {
1690*4882a593Smuzhiyun 		dev_err(pcu->dev,
1691*4882a593Smuzhiyun 			"First endpoint on data interface is not BULK OUT\n");
1692*4882a593Smuzhiyun 		return -EINVAL;
1693*4882a593Smuzhiyun 	}
1694*4882a593Smuzhiyun 
1695*4882a593Smuzhiyun 	pcu->max_out_size = usb_endpoint_maxp(pcu->ep_out);
1696*4882a593Smuzhiyun 	if (pcu->max_out_size < 8) {
1697*4882a593Smuzhiyun 		dev_err(pcu->dev,
1698*4882a593Smuzhiyun 			"Max OUT packet size is too small (%zd)\n",
1699*4882a593Smuzhiyun 			pcu->max_out_size);
1700*4882a593Smuzhiyun 		return -EINVAL;
1701*4882a593Smuzhiyun 	}
1702*4882a593Smuzhiyun 
1703*4882a593Smuzhiyun 	pcu->ep_in = &alt->endpoint[1].desc;
1704*4882a593Smuzhiyun 	if (!usb_endpoint_is_bulk_in(pcu->ep_in)) {
1705*4882a593Smuzhiyun 		dev_err(pcu->dev,
1706*4882a593Smuzhiyun 			"Second endpoint on data interface is not BULK IN\n");
1707*4882a593Smuzhiyun 		return -EINVAL;
1708*4882a593Smuzhiyun 	}
1709*4882a593Smuzhiyun 
1710*4882a593Smuzhiyun 	pcu->max_in_size = usb_endpoint_maxp(pcu->ep_in);
1711*4882a593Smuzhiyun 	if (pcu->max_in_size < 8) {
1712*4882a593Smuzhiyun 		dev_err(pcu->dev,
1713*4882a593Smuzhiyun 			"Max IN packet size is too small (%zd)\n",
1714*4882a593Smuzhiyun 			pcu->max_in_size);
1715*4882a593Smuzhiyun 		return -EINVAL;
1716*4882a593Smuzhiyun 	}
1717*4882a593Smuzhiyun 
1718*4882a593Smuzhiyun 	return 0;
1719*4882a593Smuzhiyun }
1720*4882a593Smuzhiyun 
ims_pcu_start_io(struct ims_pcu * pcu)1721*4882a593Smuzhiyun static int ims_pcu_start_io(struct ims_pcu *pcu)
1722*4882a593Smuzhiyun {
1723*4882a593Smuzhiyun 	int error;
1724*4882a593Smuzhiyun 
1725*4882a593Smuzhiyun 	error = usb_submit_urb(pcu->urb_ctrl, GFP_KERNEL);
1726*4882a593Smuzhiyun 	if (error) {
1727*4882a593Smuzhiyun 		dev_err(pcu->dev,
1728*4882a593Smuzhiyun 			"Failed to start control IO - usb_submit_urb failed with result: %d\n",
1729*4882a593Smuzhiyun 			error);
1730*4882a593Smuzhiyun 		return -EIO;
1731*4882a593Smuzhiyun 	}
1732*4882a593Smuzhiyun 
1733*4882a593Smuzhiyun 	error = usb_submit_urb(pcu->urb_in, GFP_KERNEL);
1734*4882a593Smuzhiyun 	if (error) {
1735*4882a593Smuzhiyun 		dev_err(pcu->dev,
1736*4882a593Smuzhiyun 			"Failed to start IO - usb_submit_urb failed with result: %d\n",
1737*4882a593Smuzhiyun 			error);
1738*4882a593Smuzhiyun 		usb_kill_urb(pcu->urb_ctrl);
1739*4882a593Smuzhiyun 		return -EIO;
1740*4882a593Smuzhiyun 	}
1741*4882a593Smuzhiyun 
1742*4882a593Smuzhiyun 	return 0;
1743*4882a593Smuzhiyun }
1744*4882a593Smuzhiyun 
ims_pcu_stop_io(struct ims_pcu * pcu)1745*4882a593Smuzhiyun static void ims_pcu_stop_io(struct ims_pcu *pcu)
1746*4882a593Smuzhiyun {
1747*4882a593Smuzhiyun 	usb_kill_urb(pcu->urb_in);
1748*4882a593Smuzhiyun 	usb_kill_urb(pcu->urb_ctrl);
1749*4882a593Smuzhiyun }
1750*4882a593Smuzhiyun 
ims_pcu_line_setup(struct ims_pcu * pcu)1751*4882a593Smuzhiyun static int ims_pcu_line_setup(struct ims_pcu *pcu)
1752*4882a593Smuzhiyun {
1753*4882a593Smuzhiyun 	struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting;
1754*4882a593Smuzhiyun 	struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf;
1755*4882a593Smuzhiyun 	int error;
1756*4882a593Smuzhiyun 
1757*4882a593Smuzhiyun 	memset(line, 0, sizeof(*line));
1758*4882a593Smuzhiyun 	line->dwDTERate = cpu_to_le32(57600);
1759*4882a593Smuzhiyun 	line->bDataBits = 8;
1760*4882a593Smuzhiyun 
1761*4882a593Smuzhiyun 	error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0),
1762*4882a593Smuzhiyun 				USB_CDC_REQ_SET_LINE_CODING,
1763*4882a593Smuzhiyun 				USB_TYPE_CLASS | USB_RECIP_INTERFACE,
1764*4882a593Smuzhiyun 				0, interface->desc.bInterfaceNumber,
1765*4882a593Smuzhiyun 				line, sizeof(struct usb_cdc_line_coding),
1766*4882a593Smuzhiyun 				5000);
1767*4882a593Smuzhiyun 	if (error < 0) {
1768*4882a593Smuzhiyun 		dev_err(pcu->dev, "Failed to set line coding, error: %d\n",
1769*4882a593Smuzhiyun 			error);
1770*4882a593Smuzhiyun 		return error;
1771*4882a593Smuzhiyun 	}
1772*4882a593Smuzhiyun 
1773*4882a593Smuzhiyun 	error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0),
1774*4882a593Smuzhiyun 				USB_CDC_REQ_SET_CONTROL_LINE_STATE,
1775*4882a593Smuzhiyun 				USB_TYPE_CLASS | USB_RECIP_INTERFACE,
1776*4882a593Smuzhiyun 				0x03, interface->desc.bInterfaceNumber,
1777*4882a593Smuzhiyun 				NULL, 0, 5000);
1778*4882a593Smuzhiyun 	if (error < 0) {
1779*4882a593Smuzhiyun 		dev_err(pcu->dev, "Failed to set line state, error: %d\n",
1780*4882a593Smuzhiyun 			error);
1781*4882a593Smuzhiyun 		return error;
1782*4882a593Smuzhiyun 	}
1783*4882a593Smuzhiyun 
1784*4882a593Smuzhiyun 	return 0;
1785*4882a593Smuzhiyun }
1786*4882a593Smuzhiyun 
ims_pcu_get_device_info(struct ims_pcu * pcu)1787*4882a593Smuzhiyun static int ims_pcu_get_device_info(struct ims_pcu *pcu)
1788*4882a593Smuzhiyun {
1789*4882a593Smuzhiyun 	int error;
1790*4882a593Smuzhiyun 
1791*4882a593Smuzhiyun 	error = ims_pcu_get_info(pcu);
1792*4882a593Smuzhiyun 	if (error)
1793*4882a593Smuzhiyun 		return error;
1794*4882a593Smuzhiyun 
1795*4882a593Smuzhiyun 	error = ims_pcu_execute_query(pcu, GET_FW_VERSION);
1796*4882a593Smuzhiyun 	if (error) {
1797*4882a593Smuzhiyun 		dev_err(pcu->dev,
1798*4882a593Smuzhiyun 			"GET_FW_VERSION command failed, error: %d\n", error);
1799*4882a593Smuzhiyun 		return error;
1800*4882a593Smuzhiyun 	}
1801*4882a593Smuzhiyun 
1802*4882a593Smuzhiyun 	snprintf(pcu->fw_version, sizeof(pcu->fw_version),
1803*4882a593Smuzhiyun 		 "%02d%02d%02d%02d.%c%c",
1804*4882a593Smuzhiyun 		 pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5],
1805*4882a593Smuzhiyun 		 pcu->cmd_buf[6], pcu->cmd_buf[7]);
1806*4882a593Smuzhiyun 
1807*4882a593Smuzhiyun 	error = ims_pcu_execute_query(pcu, GET_BL_VERSION);
1808*4882a593Smuzhiyun 	if (error) {
1809*4882a593Smuzhiyun 		dev_err(pcu->dev,
1810*4882a593Smuzhiyun 			"GET_BL_VERSION command failed, error: %d\n", error);
1811*4882a593Smuzhiyun 		return error;
1812*4882a593Smuzhiyun 	}
1813*4882a593Smuzhiyun 
1814*4882a593Smuzhiyun 	snprintf(pcu->bl_version, sizeof(pcu->bl_version),
1815*4882a593Smuzhiyun 		 "%02d%02d%02d%02d.%c%c",
1816*4882a593Smuzhiyun 		 pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5],
1817*4882a593Smuzhiyun 		 pcu->cmd_buf[6], pcu->cmd_buf[7]);
1818*4882a593Smuzhiyun 
1819*4882a593Smuzhiyun 	error = ims_pcu_execute_query(pcu, RESET_REASON);
1820*4882a593Smuzhiyun 	if (error) {
1821*4882a593Smuzhiyun 		dev_err(pcu->dev,
1822*4882a593Smuzhiyun 			"RESET_REASON command failed, error: %d\n", error);
1823*4882a593Smuzhiyun 		return error;
1824*4882a593Smuzhiyun 	}
1825*4882a593Smuzhiyun 
1826*4882a593Smuzhiyun 	snprintf(pcu->reset_reason, sizeof(pcu->reset_reason),
1827*4882a593Smuzhiyun 		 "%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
1828*4882a593Smuzhiyun 
1829*4882a593Smuzhiyun 	dev_dbg(pcu->dev,
1830*4882a593Smuzhiyun 		"P/N: %s, MD: %s, S/N: %s, FW: %s, BL: %s, RR: %s\n",
1831*4882a593Smuzhiyun 		pcu->part_number,
1832*4882a593Smuzhiyun 		pcu->date_of_manufacturing,
1833*4882a593Smuzhiyun 		pcu->serial_number,
1834*4882a593Smuzhiyun 		pcu->fw_version,
1835*4882a593Smuzhiyun 		pcu->bl_version,
1836*4882a593Smuzhiyun 		pcu->reset_reason);
1837*4882a593Smuzhiyun 
1838*4882a593Smuzhiyun 	return 0;
1839*4882a593Smuzhiyun }
1840*4882a593Smuzhiyun 
ims_pcu_identify_type(struct ims_pcu * pcu,u8 * device_id)1841*4882a593Smuzhiyun static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id)
1842*4882a593Smuzhiyun {
1843*4882a593Smuzhiyun 	int error;
1844*4882a593Smuzhiyun 
1845*4882a593Smuzhiyun 	error = ims_pcu_execute_query(pcu, GET_DEVICE_ID);
1846*4882a593Smuzhiyun 	if (error) {
1847*4882a593Smuzhiyun 		dev_err(pcu->dev,
1848*4882a593Smuzhiyun 			"GET_DEVICE_ID command failed, error: %d\n", error);
1849*4882a593Smuzhiyun 		return error;
1850*4882a593Smuzhiyun 	}
1851*4882a593Smuzhiyun 
1852*4882a593Smuzhiyun 	*device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET];
1853*4882a593Smuzhiyun 	dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id);
1854*4882a593Smuzhiyun 
1855*4882a593Smuzhiyun 	return 0;
1856*4882a593Smuzhiyun }
1857*4882a593Smuzhiyun 
ims_pcu_init_application_mode(struct ims_pcu * pcu)1858*4882a593Smuzhiyun static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
1859*4882a593Smuzhiyun {
1860*4882a593Smuzhiyun 	static atomic_t device_no = ATOMIC_INIT(-1);
1861*4882a593Smuzhiyun 
1862*4882a593Smuzhiyun 	const struct ims_pcu_device_info *info;
1863*4882a593Smuzhiyun 	int error;
1864*4882a593Smuzhiyun 
1865*4882a593Smuzhiyun 	error = ims_pcu_get_device_info(pcu);
1866*4882a593Smuzhiyun 	if (error) {
1867*4882a593Smuzhiyun 		/* Device does not respond to basic queries, hopeless */
1868*4882a593Smuzhiyun 		return error;
1869*4882a593Smuzhiyun 	}
1870*4882a593Smuzhiyun 
1871*4882a593Smuzhiyun 	error = ims_pcu_identify_type(pcu, &pcu->device_id);
1872*4882a593Smuzhiyun 	if (error) {
1873*4882a593Smuzhiyun 		dev_err(pcu->dev,
1874*4882a593Smuzhiyun 			"Failed to identify device, error: %d\n", error);
1875*4882a593Smuzhiyun 		/*
1876*4882a593Smuzhiyun 		 * Do not signal error, but do not create input nor
1877*4882a593Smuzhiyun 		 * backlight devices either, let userspace figure this
1878*4882a593Smuzhiyun 		 * out (flash a new firmware?).
1879*4882a593Smuzhiyun 		 */
1880*4882a593Smuzhiyun 		return 0;
1881*4882a593Smuzhiyun 	}
1882*4882a593Smuzhiyun 
1883*4882a593Smuzhiyun 	if (pcu->device_id >= ARRAY_SIZE(ims_pcu_device_info) ||
1884*4882a593Smuzhiyun 	    !ims_pcu_device_info[pcu->device_id].keymap) {
1885*4882a593Smuzhiyun 		dev_err(pcu->dev, "Device ID %d is not valid\n", pcu->device_id);
1886*4882a593Smuzhiyun 		/* Same as above, punt to userspace */
1887*4882a593Smuzhiyun 		return 0;
1888*4882a593Smuzhiyun 	}
1889*4882a593Smuzhiyun 
1890*4882a593Smuzhiyun 	/* Device appears to be operable, complete initialization */
1891*4882a593Smuzhiyun 	pcu->device_no = atomic_inc_return(&device_no);
1892*4882a593Smuzhiyun 
1893*4882a593Smuzhiyun 	/*
1894*4882a593Smuzhiyun 	 * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor
1895*4882a593Smuzhiyun 	 */
1896*4882a593Smuzhiyun 	if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) {
1897*4882a593Smuzhiyun 		error = sysfs_create_group(&pcu->dev->kobj,
1898*4882a593Smuzhiyun 					   &ims_pcu_ofn_attr_group);
1899*4882a593Smuzhiyun 		if (error)
1900*4882a593Smuzhiyun 			return error;
1901*4882a593Smuzhiyun 	}
1902*4882a593Smuzhiyun 
1903*4882a593Smuzhiyun 	error = ims_pcu_setup_backlight(pcu);
1904*4882a593Smuzhiyun 	if (error)
1905*4882a593Smuzhiyun 		return error;
1906*4882a593Smuzhiyun 
1907*4882a593Smuzhiyun 	info = &ims_pcu_device_info[pcu->device_id];
1908*4882a593Smuzhiyun 	error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len);
1909*4882a593Smuzhiyun 	if (error)
1910*4882a593Smuzhiyun 		goto err_destroy_backlight;
1911*4882a593Smuzhiyun 
1912*4882a593Smuzhiyun 	if (info->has_gamepad) {
1913*4882a593Smuzhiyun 		error = ims_pcu_setup_gamepad(pcu);
1914*4882a593Smuzhiyun 		if (error)
1915*4882a593Smuzhiyun 			goto err_destroy_buttons;
1916*4882a593Smuzhiyun 	}
1917*4882a593Smuzhiyun 
1918*4882a593Smuzhiyun 	pcu->setup_complete = true;
1919*4882a593Smuzhiyun 
1920*4882a593Smuzhiyun 	return 0;
1921*4882a593Smuzhiyun 
1922*4882a593Smuzhiyun err_destroy_buttons:
1923*4882a593Smuzhiyun 	ims_pcu_destroy_buttons(pcu);
1924*4882a593Smuzhiyun err_destroy_backlight:
1925*4882a593Smuzhiyun 	ims_pcu_destroy_backlight(pcu);
1926*4882a593Smuzhiyun 	return error;
1927*4882a593Smuzhiyun }
1928*4882a593Smuzhiyun 
ims_pcu_destroy_application_mode(struct ims_pcu * pcu)1929*4882a593Smuzhiyun static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu)
1930*4882a593Smuzhiyun {
1931*4882a593Smuzhiyun 	if (pcu->setup_complete) {
1932*4882a593Smuzhiyun 		pcu->setup_complete = false;
1933*4882a593Smuzhiyun 		mb(); /* make sure flag setting is not reordered */
1934*4882a593Smuzhiyun 
1935*4882a593Smuzhiyun 		if (pcu->gamepad)
1936*4882a593Smuzhiyun 			ims_pcu_destroy_gamepad(pcu);
1937*4882a593Smuzhiyun 		ims_pcu_destroy_buttons(pcu);
1938*4882a593Smuzhiyun 		ims_pcu_destroy_backlight(pcu);
1939*4882a593Smuzhiyun 
1940*4882a593Smuzhiyun 		if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID)
1941*4882a593Smuzhiyun 			sysfs_remove_group(&pcu->dev->kobj,
1942*4882a593Smuzhiyun 					   &ims_pcu_ofn_attr_group);
1943*4882a593Smuzhiyun 	}
1944*4882a593Smuzhiyun }
1945*4882a593Smuzhiyun 
ims_pcu_init_bootloader_mode(struct ims_pcu * pcu)1946*4882a593Smuzhiyun static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu)
1947*4882a593Smuzhiyun {
1948*4882a593Smuzhiyun 	int error;
1949*4882a593Smuzhiyun 
1950*4882a593Smuzhiyun 	error = ims_pcu_execute_bl_command(pcu, QUERY_DEVICE, NULL, 0,
1951*4882a593Smuzhiyun 					   IMS_PCU_CMD_RESPONSE_TIMEOUT);
1952*4882a593Smuzhiyun 	if (error) {
1953*4882a593Smuzhiyun 		dev_err(pcu->dev, "Bootloader does not respond, aborting\n");
1954*4882a593Smuzhiyun 		return error;
1955*4882a593Smuzhiyun 	}
1956*4882a593Smuzhiyun 
1957*4882a593Smuzhiyun 	pcu->fw_start_addr =
1958*4882a593Smuzhiyun 		get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]);
1959*4882a593Smuzhiyun 	pcu->fw_end_addr =
1960*4882a593Smuzhiyun 		get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 15]);
1961*4882a593Smuzhiyun 
1962*4882a593Smuzhiyun 	dev_info(pcu->dev,
1963*4882a593Smuzhiyun 		 "Device is in bootloader mode (addr 0x%08x-0x%08x), requesting firmware\n",
1964*4882a593Smuzhiyun 		 pcu->fw_start_addr, pcu->fw_end_addr);
1965*4882a593Smuzhiyun 
1966*4882a593Smuzhiyun 	error = request_firmware_nowait(THIS_MODULE, true,
1967*4882a593Smuzhiyun 					IMS_PCU_FIRMWARE_NAME,
1968*4882a593Smuzhiyun 					pcu->dev, GFP_KERNEL, pcu,
1969*4882a593Smuzhiyun 					ims_pcu_process_async_firmware);
1970*4882a593Smuzhiyun 	if (error) {
1971*4882a593Smuzhiyun 		/* This error is not fatal, let userspace have another chance */
1972*4882a593Smuzhiyun 		complete(&pcu->async_firmware_done);
1973*4882a593Smuzhiyun 	}
1974*4882a593Smuzhiyun 
1975*4882a593Smuzhiyun 	return 0;
1976*4882a593Smuzhiyun }
1977*4882a593Smuzhiyun 
ims_pcu_destroy_bootloader_mode(struct ims_pcu * pcu)1978*4882a593Smuzhiyun static void ims_pcu_destroy_bootloader_mode(struct ims_pcu *pcu)
1979*4882a593Smuzhiyun {
1980*4882a593Smuzhiyun 	/* Make sure our initial firmware request has completed */
1981*4882a593Smuzhiyun 	wait_for_completion(&pcu->async_firmware_done);
1982*4882a593Smuzhiyun }
1983*4882a593Smuzhiyun 
1984*4882a593Smuzhiyun #define IMS_PCU_APPLICATION_MODE	0
1985*4882a593Smuzhiyun #define IMS_PCU_BOOTLOADER_MODE		1
1986*4882a593Smuzhiyun 
1987*4882a593Smuzhiyun static struct usb_driver ims_pcu_driver;
1988*4882a593Smuzhiyun 
ims_pcu_probe(struct usb_interface * intf,const struct usb_device_id * id)1989*4882a593Smuzhiyun static int ims_pcu_probe(struct usb_interface *intf,
1990*4882a593Smuzhiyun 			 const struct usb_device_id *id)
1991*4882a593Smuzhiyun {
1992*4882a593Smuzhiyun 	struct usb_device *udev = interface_to_usbdev(intf);
1993*4882a593Smuzhiyun 	struct ims_pcu *pcu;
1994*4882a593Smuzhiyun 	int error;
1995*4882a593Smuzhiyun 
1996*4882a593Smuzhiyun 	pcu = kzalloc(sizeof(struct ims_pcu), GFP_KERNEL);
1997*4882a593Smuzhiyun 	if (!pcu)
1998*4882a593Smuzhiyun 		return -ENOMEM;
1999*4882a593Smuzhiyun 
2000*4882a593Smuzhiyun 	pcu->dev = &intf->dev;
2001*4882a593Smuzhiyun 	pcu->udev = udev;
2002*4882a593Smuzhiyun 	pcu->bootloader_mode = id->driver_info == IMS_PCU_BOOTLOADER_MODE;
2003*4882a593Smuzhiyun 	mutex_init(&pcu->cmd_mutex);
2004*4882a593Smuzhiyun 	init_completion(&pcu->cmd_done);
2005*4882a593Smuzhiyun 	init_completion(&pcu->async_firmware_done);
2006*4882a593Smuzhiyun 
2007*4882a593Smuzhiyun 	error = ims_pcu_parse_cdc_data(intf, pcu);
2008*4882a593Smuzhiyun 	if (error)
2009*4882a593Smuzhiyun 		goto err_free_mem;
2010*4882a593Smuzhiyun 
2011*4882a593Smuzhiyun 	error = usb_driver_claim_interface(&ims_pcu_driver,
2012*4882a593Smuzhiyun 					   pcu->data_intf, pcu);
2013*4882a593Smuzhiyun 	if (error) {
2014*4882a593Smuzhiyun 		dev_err(&intf->dev,
2015*4882a593Smuzhiyun 			"Unable to claim corresponding data interface: %d\n",
2016*4882a593Smuzhiyun 			error);
2017*4882a593Smuzhiyun 		goto err_free_mem;
2018*4882a593Smuzhiyun 	}
2019*4882a593Smuzhiyun 
2020*4882a593Smuzhiyun 	usb_set_intfdata(pcu->ctrl_intf, pcu);
2021*4882a593Smuzhiyun 	usb_set_intfdata(pcu->data_intf, pcu);
2022*4882a593Smuzhiyun 
2023*4882a593Smuzhiyun 	error = ims_pcu_buffers_alloc(pcu);
2024*4882a593Smuzhiyun 	if (error)
2025*4882a593Smuzhiyun 		goto err_unclaim_intf;
2026*4882a593Smuzhiyun 
2027*4882a593Smuzhiyun 	error = ims_pcu_start_io(pcu);
2028*4882a593Smuzhiyun 	if (error)
2029*4882a593Smuzhiyun 		goto err_free_buffers;
2030*4882a593Smuzhiyun 
2031*4882a593Smuzhiyun 	error = ims_pcu_line_setup(pcu);
2032*4882a593Smuzhiyun 	if (error)
2033*4882a593Smuzhiyun 		goto err_stop_io;
2034*4882a593Smuzhiyun 
2035*4882a593Smuzhiyun 	error = sysfs_create_group(&intf->dev.kobj, &ims_pcu_attr_group);
2036*4882a593Smuzhiyun 	if (error)
2037*4882a593Smuzhiyun 		goto err_stop_io;
2038*4882a593Smuzhiyun 
2039*4882a593Smuzhiyun 	error = pcu->bootloader_mode ?
2040*4882a593Smuzhiyun 			ims_pcu_init_bootloader_mode(pcu) :
2041*4882a593Smuzhiyun 			ims_pcu_init_application_mode(pcu);
2042*4882a593Smuzhiyun 	if (error)
2043*4882a593Smuzhiyun 		goto err_remove_sysfs;
2044*4882a593Smuzhiyun 
2045*4882a593Smuzhiyun 	return 0;
2046*4882a593Smuzhiyun 
2047*4882a593Smuzhiyun err_remove_sysfs:
2048*4882a593Smuzhiyun 	sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
2049*4882a593Smuzhiyun err_stop_io:
2050*4882a593Smuzhiyun 	ims_pcu_stop_io(pcu);
2051*4882a593Smuzhiyun err_free_buffers:
2052*4882a593Smuzhiyun 	ims_pcu_buffers_free(pcu);
2053*4882a593Smuzhiyun err_unclaim_intf:
2054*4882a593Smuzhiyun 	usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf);
2055*4882a593Smuzhiyun err_free_mem:
2056*4882a593Smuzhiyun 	kfree(pcu);
2057*4882a593Smuzhiyun 	return error;
2058*4882a593Smuzhiyun }
2059*4882a593Smuzhiyun 
ims_pcu_disconnect(struct usb_interface * intf)2060*4882a593Smuzhiyun static void ims_pcu_disconnect(struct usb_interface *intf)
2061*4882a593Smuzhiyun {
2062*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
2063*4882a593Smuzhiyun 	struct usb_host_interface *alt = intf->cur_altsetting;
2064*4882a593Smuzhiyun 
2065*4882a593Smuzhiyun 	usb_set_intfdata(intf, NULL);
2066*4882a593Smuzhiyun 
2067*4882a593Smuzhiyun 	/*
2068*4882a593Smuzhiyun 	 * See if we are dealing with control or data interface. The cleanup
2069*4882a593Smuzhiyun 	 * happens when we unbind primary (control) interface.
2070*4882a593Smuzhiyun 	 */
2071*4882a593Smuzhiyun 	if (alt->desc.bInterfaceClass != USB_CLASS_COMM)
2072*4882a593Smuzhiyun 		return;
2073*4882a593Smuzhiyun 
2074*4882a593Smuzhiyun 	sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
2075*4882a593Smuzhiyun 
2076*4882a593Smuzhiyun 	ims_pcu_stop_io(pcu);
2077*4882a593Smuzhiyun 
2078*4882a593Smuzhiyun 	if (pcu->bootloader_mode)
2079*4882a593Smuzhiyun 		ims_pcu_destroy_bootloader_mode(pcu);
2080*4882a593Smuzhiyun 	else
2081*4882a593Smuzhiyun 		ims_pcu_destroy_application_mode(pcu);
2082*4882a593Smuzhiyun 
2083*4882a593Smuzhiyun 	ims_pcu_buffers_free(pcu);
2084*4882a593Smuzhiyun 	kfree(pcu);
2085*4882a593Smuzhiyun }
2086*4882a593Smuzhiyun 
2087*4882a593Smuzhiyun #ifdef CONFIG_PM
ims_pcu_suspend(struct usb_interface * intf,pm_message_t message)2088*4882a593Smuzhiyun static int ims_pcu_suspend(struct usb_interface *intf,
2089*4882a593Smuzhiyun 			   pm_message_t message)
2090*4882a593Smuzhiyun {
2091*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
2092*4882a593Smuzhiyun 	struct usb_host_interface *alt = intf->cur_altsetting;
2093*4882a593Smuzhiyun 
2094*4882a593Smuzhiyun 	if (alt->desc.bInterfaceClass == USB_CLASS_COMM)
2095*4882a593Smuzhiyun 		ims_pcu_stop_io(pcu);
2096*4882a593Smuzhiyun 
2097*4882a593Smuzhiyun 	return 0;
2098*4882a593Smuzhiyun }
2099*4882a593Smuzhiyun 
ims_pcu_resume(struct usb_interface * intf)2100*4882a593Smuzhiyun static int ims_pcu_resume(struct usb_interface *intf)
2101*4882a593Smuzhiyun {
2102*4882a593Smuzhiyun 	struct ims_pcu *pcu = usb_get_intfdata(intf);
2103*4882a593Smuzhiyun 	struct usb_host_interface *alt = intf->cur_altsetting;
2104*4882a593Smuzhiyun 	int retval = 0;
2105*4882a593Smuzhiyun 
2106*4882a593Smuzhiyun 	if (alt->desc.bInterfaceClass == USB_CLASS_COMM) {
2107*4882a593Smuzhiyun 		retval = ims_pcu_start_io(pcu);
2108*4882a593Smuzhiyun 		if (retval == 0)
2109*4882a593Smuzhiyun 			retval = ims_pcu_line_setup(pcu);
2110*4882a593Smuzhiyun 	}
2111*4882a593Smuzhiyun 
2112*4882a593Smuzhiyun 	return retval;
2113*4882a593Smuzhiyun }
2114*4882a593Smuzhiyun #endif
2115*4882a593Smuzhiyun 
2116*4882a593Smuzhiyun static const struct usb_device_id ims_pcu_id_table[] = {
2117*4882a593Smuzhiyun 	{
2118*4882a593Smuzhiyun 		USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0082,
2119*4882a593Smuzhiyun 					USB_CLASS_COMM,
2120*4882a593Smuzhiyun 					USB_CDC_SUBCLASS_ACM,
2121*4882a593Smuzhiyun 					USB_CDC_ACM_PROTO_AT_V25TER),
2122*4882a593Smuzhiyun 		.driver_info = IMS_PCU_APPLICATION_MODE,
2123*4882a593Smuzhiyun 	},
2124*4882a593Smuzhiyun 	{
2125*4882a593Smuzhiyun 		USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0083,
2126*4882a593Smuzhiyun 					USB_CLASS_COMM,
2127*4882a593Smuzhiyun 					USB_CDC_SUBCLASS_ACM,
2128*4882a593Smuzhiyun 					USB_CDC_ACM_PROTO_AT_V25TER),
2129*4882a593Smuzhiyun 		.driver_info = IMS_PCU_BOOTLOADER_MODE,
2130*4882a593Smuzhiyun 	},
2131*4882a593Smuzhiyun 	{ }
2132*4882a593Smuzhiyun };
2133*4882a593Smuzhiyun 
2134*4882a593Smuzhiyun static struct usb_driver ims_pcu_driver = {
2135*4882a593Smuzhiyun 	.name			= "ims_pcu",
2136*4882a593Smuzhiyun 	.id_table		= ims_pcu_id_table,
2137*4882a593Smuzhiyun 	.probe			= ims_pcu_probe,
2138*4882a593Smuzhiyun 	.disconnect		= ims_pcu_disconnect,
2139*4882a593Smuzhiyun #ifdef CONFIG_PM
2140*4882a593Smuzhiyun 	.suspend		= ims_pcu_suspend,
2141*4882a593Smuzhiyun 	.resume			= ims_pcu_resume,
2142*4882a593Smuzhiyun 	.reset_resume		= ims_pcu_resume,
2143*4882a593Smuzhiyun #endif
2144*4882a593Smuzhiyun };
2145*4882a593Smuzhiyun 
2146*4882a593Smuzhiyun module_usb_driver(ims_pcu_driver);
2147*4882a593Smuzhiyun 
2148*4882a593Smuzhiyun MODULE_DESCRIPTION("IMS Passenger Control Unit driver");
2149*4882a593Smuzhiyun MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
2150*4882a593Smuzhiyun MODULE_LICENSE("GPL");
2151