xref: /linux/drivers/iio/common/ssp_sensors/ssp_dev.c (revision bb08abc7)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
250dd64d5SKarol Wrona /*
350dd64d5SKarol Wrona  *  Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
450dd64d5SKarol Wrona  */
550dd64d5SKarol Wrona 
650dd64d5SKarol Wrona #include <linux/iio/iio.h>
750dd64d5SKarol Wrona #include <linux/interrupt.h>
850dd64d5SKarol Wrona #include <linux/io.h>
950dd64d5SKarol Wrona #include <linux/mfd/core.h>
10b946e949SAndy Shevchenko #include <linux/mod_devicetable.h>
1150dd64d5SKarol Wrona #include <linux/module.h>
12b946e949SAndy Shevchenko #include <linux/property.h>
13b946e949SAndy Shevchenko 
1450dd64d5SKarol Wrona #include "ssp.h"
1550dd64d5SKarol Wrona 
1650dd64d5SKarol Wrona #define SSP_WDT_TIME			10000
1750dd64d5SKarol Wrona #define SSP_LIMIT_RESET_CNT		20
1850dd64d5SKarol Wrona #define SSP_LIMIT_TIMEOUT_CNT		3
1950dd64d5SKarol Wrona 
2050dd64d5SKarol Wrona /* It is possible that it is max clk rate for version 1.0 of bootcode */
2150dd64d5SKarol Wrona #define SSP_BOOT_SPI_HZ	400000
2250dd64d5SKarol Wrona 
2350dd64d5SKarol Wrona /*
2450dd64d5SKarol Wrona  * These fields can look enigmatic but this structure is used mainly to flat
2550dd64d5SKarol Wrona  * some values and depends on command type.
2650dd64d5SKarol Wrona  */
2750dd64d5SKarol Wrona struct ssp_instruction {
2850dd64d5SKarol Wrona 	__le32 a;
2950dd64d5SKarol Wrona 	__le32 b;
3050dd64d5SKarol Wrona 	u8 c;
3150dd64d5SKarol Wrona } __attribute__((__packed__));
3250dd64d5SKarol Wrona 
3350dd64d5SKarol Wrona static const u8 ssp_magnitude_table[] = {110, 85, 171, 71, 203, 195, 0, 67,
3450dd64d5SKarol Wrona 	208, 56, 175, 244, 206, 213, 0, 92, 250, 0, 55, 48, 189, 252, 171,
3550dd64d5SKarol Wrona 	243, 13, 45, 250};
3650dd64d5SKarol Wrona 
3750dd64d5SKarol Wrona static const struct ssp_sensorhub_info ssp_rinato_info = {
3850dd64d5SKarol Wrona 	.fw_name = "ssp_B2.fw",
3950dd64d5SKarol Wrona 	.fw_crashed_name = "ssp_crashed.fw",
4050dd64d5SKarol Wrona 	.fw_rev = 14052300,
4150dd64d5SKarol Wrona 	.mag_table = ssp_magnitude_table,
4250dd64d5SKarol Wrona 	.mag_length = ARRAY_SIZE(ssp_magnitude_table),
4350dd64d5SKarol Wrona };
4450dd64d5SKarol Wrona 
4550dd64d5SKarol Wrona static const struct ssp_sensorhub_info ssp_thermostat_info = {
4650dd64d5SKarol Wrona 	.fw_name = "thermostat_B2.fw",
4750dd64d5SKarol Wrona 	.fw_crashed_name = "ssp_crashed.fw",
4850dd64d5SKarol Wrona 	.fw_rev = 14080600,
4950dd64d5SKarol Wrona 	.mag_table = ssp_magnitude_table,
5050dd64d5SKarol Wrona 	.mag_length = ARRAY_SIZE(ssp_magnitude_table),
5150dd64d5SKarol Wrona };
5250dd64d5SKarol Wrona 
5350dd64d5SKarol Wrona static const struct mfd_cell sensorhub_sensor_devs[] = {
5450dd64d5SKarol Wrona 	{
5550dd64d5SKarol Wrona 		.name = "ssp-accelerometer",
5650dd64d5SKarol Wrona 	},
5750dd64d5SKarol Wrona 	{
5850dd64d5SKarol Wrona 		.name = "ssp-gyroscope",
5950dd64d5SKarol Wrona 	},
6050dd64d5SKarol Wrona };
6150dd64d5SKarol Wrona 
ssp_toggle_mcu_reset_gpio(struct ssp_data * data)6250dd64d5SKarol Wrona static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data)
6350dd64d5SKarol Wrona {
644cf01d6dSLinus Walleij 	gpiod_set_value(data->mcu_reset_gpiod, 0);
6550dd64d5SKarol Wrona 	usleep_range(1000, 1200);
664cf01d6dSLinus Walleij 	gpiod_set_value(data->mcu_reset_gpiod, 1);
6750dd64d5SKarol Wrona 	msleep(50);
6850dd64d5SKarol Wrona }
6950dd64d5SKarol Wrona 
ssp_sync_available_sensors(struct ssp_data * data)7050dd64d5SKarol Wrona static void ssp_sync_available_sensors(struct ssp_data *data)
7150dd64d5SKarol Wrona {
7250dd64d5SKarol Wrona 	int i, ret;
7350dd64d5SKarol Wrona 
7450dd64d5SKarol Wrona 	for (i = 0; i < SSP_SENSOR_MAX; ++i) {
7550dd64d5SKarol Wrona 		if (data->available_sensors & BIT(i)) {
7650dd64d5SKarol Wrona 			ret = ssp_enable_sensor(data, i, data->delay_buf[i]);
7750dd64d5SKarol Wrona 			if (ret < 0) {
7850dd64d5SKarol Wrona 				dev_err(&data->spi->dev,
7950dd64d5SKarol Wrona 					"Sync sensor nr: %d fail\n", i);
8050dd64d5SKarol Wrona 				continue;
8150dd64d5SKarol Wrona 			}
8250dd64d5SKarol Wrona 		}
8350dd64d5SKarol Wrona 	}
8450dd64d5SKarol Wrona 
8550dd64d5SKarol Wrona 	ret = ssp_command(data, SSP_MSG2SSP_AP_MCU_SET_DUMPMODE,
8650dd64d5SKarol Wrona 			  data->mcu_dump_mode);
8750dd64d5SKarol Wrona 	if (ret < 0)
8850dd64d5SKarol Wrona 		dev_err(&data->spi->dev,
8950dd64d5SKarol Wrona 			"SSP_MSG2SSP_AP_MCU_SET_DUMPMODE failed\n");
9050dd64d5SKarol Wrona }
9150dd64d5SKarol Wrona 
ssp_enable_mcu(struct ssp_data * data,bool enable)9250dd64d5SKarol Wrona static void ssp_enable_mcu(struct ssp_data *data, bool enable)
9350dd64d5SKarol Wrona {
9450dd64d5SKarol Wrona 	dev_info(&data->spi->dev, "current shutdown = %d, old = %d\n", enable,
9550dd64d5SKarol Wrona 		 data->shut_down);
9650dd64d5SKarol Wrona 
9750dd64d5SKarol Wrona 	if (enable && data->shut_down) {
9850dd64d5SKarol Wrona 		data->shut_down = false;
9950dd64d5SKarol Wrona 		enable_irq(data->spi->irq);
10050dd64d5SKarol Wrona 		enable_irq_wake(data->spi->irq);
10150dd64d5SKarol Wrona 	} else if (!enable && !data->shut_down) {
10250dd64d5SKarol Wrona 		data->shut_down = true;
10350dd64d5SKarol Wrona 		disable_irq(data->spi->irq);
10450dd64d5SKarol Wrona 		disable_irq_wake(data->spi->irq);
10550dd64d5SKarol Wrona 	} else {
10650dd64d5SKarol Wrona 		dev_warn(&data->spi->dev, "current shutdown = %d, old = %d\n",
10750dd64d5SKarol Wrona 			 enable, data->shut_down);
10850dd64d5SKarol Wrona 	}
10950dd64d5SKarol Wrona }
11050dd64d5SKarol Wrona 
11150dd64d5SKarol Wrona /*
11250dd64d5SKarol Wrona  * This function is the first one which communicates with the mcu so it is
11350dd64d5SKarol Wrona  * possible that the first attempt will fail
11450dd64d5SKarol Wrona  */
ssp_check_fwbl(struct ssp_data * data)11550dd64d5SKarol Wrona static int ssp_check_fwbl(struct ssp_data *data)
11650dd64d5SKarol Wrona {
11750dd64d5SKarol Wrona 	int retries = 0;
11850dd64d5SKarol Wrona 
11950dd64d5SKarol Wrona 	while (retries++ < 5) {
12050dd64d5SKarol Wrona 		data->cur_firm_rev = ssp_get_firmware_rev(data);
12150dd64d5SKarol Wrona 		if (data->cur_firm_rev == SSP_INVALID_REVISION ||
12250dd64d5SKarol Wrona 		    data->cur_firm_rev == SSP_INVALID_REVISION2) {
12350dd64d5SKarol Wrona 			dev_warn(&data->spi->dev,
12450dd64d5SKarol Wrona 				 "Invalid revision, trying %d time\n", retries);
12550dd64d5SKarol Wrona 		} else {
12650dd64d5SKarol Wrona 			break;
12750dd64d5SKarol Wrona 		}
12850dd64d5SKarol Wrona 	}
12950dd64d5SKarol Wrona 
13050dd64d5SKarol Wrona 	if (data->cur_firm_rev == SSP_INVALID_REVISION ||
13150dd64d5SKarol Wrona 	    data->cur_firm_rev == SSP_INVALID_REVISION2) {
13250dd64d5SKarol Wrona 		dev_err(&data->spi->dev, "SSP_INVALID_REVISION\n");
13350dd64d5SKarol Wrona 		return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
13450dd64d5SKarol Wrona 	}
13550dd64d5SKarol Wrona 
13650dd64d5SKarol Wrona 	dev_info(&data->spi->dev,
13750dd64d5SKarol Wrona 		 "MCU Firm Rev : Old = %8u, New = %8u\n",
13850dd64d5SKarol Wrona 		 data->cur_firm_rev,
13950dd64d5SKarol Wrona 		 data->sensorhub_info->fw_rev);
14050dd64d5SKarol Wrona 
14150dd64d5SKarol Wrona 	if (data->cur_firm_rev != data->sensorhub_info->fw_rev)
14250dd64d5SKarol Wrona 		return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
14350dd64d5SKarol Wrona 
14450dd64d5SKarol Wrona 	return SSP_FW_DL_STATE_NONE;
14550dd64d5SKarol Wrona }
14650dd64d5SKarol Wrona 
ssp_reset_mcu(struct ssp_data * data)14750dd64d5SKarol Wrona static void ssp_reset_mcu(struct ssp_data *data)
14850dd64d5SKarol Wrona {
14950dd64d5SKarol Wrona 	ssp_enable_mcu(data, false);
15050dd64d5SKarol Wrona 	ssp_clean_pending_list(data);
15150dd64d5SKarol Wrona 	ssp_toggle_mcu_reset_gpio(data);
15250dd64d5SKarol Wrona 	ssp_enable_mcu(data, true);
15350dd64d5SKarol Wrona }
15450dd64d5SKarol Wrona 
ssp_wdt_work_func(struct work_struct * work)15550dd64d5SKarol Wrona static void ssp_wdt_work_func(struct work_struct *work)
15650dd64d5SKarol Wrona {
15750dd64d5SKarol Wrona 	struct ssp_data *data = container_of(work, struct ssp_data, work_wdt);
15850dd64d5SKarol Wrona 
15950dd64d5SKarol Wrona 	dev_err(&data->spi->dev, "%s - Sensor state: 0x%x, RC: %u, CC: %u\n",
16050dd64d5SKarol Wrona 		__func__, data->available_sensors, data->reset_cnt,
16150dd64d5SKarol Wrona 		data->com_fail_cnt);
16250dd64d5SKarol Wrona 
16350dd64d5SKarol Wrona 	ssp_reset_mcu(data);
16450dd64d5SKarol Wrona 	data->com_fail_cnt = 0;
16550dd64d5SKarol Wrona 	data->timeout_cnt = 0;
16650dd64d5SKarol Wrona }
16750dd64d5SKarol Wrona 
ssp_wdt_timer_func(struct timer_list * t)168e99e88a9SKees Cook static void ssp_wdt_timer_func(struct timer_list *t)
16950dd64d5SKarol Wrona {
170e99e88a9SKees Cook 	struct ssp_data *data = from_timer(data, t, wdt_timer);
17150dd64d5SKarol Wrona 
17250dd64d5SKarol Wrona 	switch (data->fw_dl_state) {
17350dd64d5SKarol Wrona 	case SSP_FW_DL_STATE_FAIL:
17450dd64d5SKarol Wrona 	case SSP_FW_DL_STATE_DOWNLOADING:
17550dd64d5SKarol Wrona 	case SSP_FW_DL_STATE_SYNC:
17650dd64d5SKarol Wrona 		goto _mod;
17750dd64d5SKarol Wrona 	}
17850dd64d5SKarol Wrona 
17950dd64d5SKarol Wrona 	if (data->timeout_cnt > SSP_LIMIT_TIMEOUT_CNT ||
18050dd64d5SKarol Wrona 	    data->com_fail_cnt > SSP_LIMIT_RESET_CNT)
18150dd64d5SKarol Wrona 		queue_work(system_power_efficient_wq, &data->work_wdt);
18250dd64d5SKarol Wrona _mod:
18350dd64d5SKarol Wrona 	mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
18450dd64d5SKarol Wrona }
18550dd64d5SKarol Wrona 
ssp_enable_wdt_timer(struct ssp_data * data)18650dd64d5SKarol Wrona static void ssp_enable_wdt_timer(struct ssp_data *data)
18750dd64d5SKarol Wrona {
18850dd64d5SKarol Wrona 	mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
18950dd64d5SKarol Wrona }
19050dd64d5SKarol Wrona 
ssp_disable_wdt_timer(struct ssp_data * data)19150dd64d5SKarol Wrona static void ssp_disable_wdt_timer(struct ssp_data *data)
19250dd64d5SKarol Wrona {
19350dd64d5SKarol Wrona 	del_timer_sync(&data->wdt_timer);
19450dd64d5SKarol Wrona 	cancel_work_sync(&data->work_wdt);
19550dd64d5SKarol Wrona }
19650dd64d5SKarol Wrona 
19750dd64d5SKarol Wrona /**
19850dd64d5SKarol Wrona  * ssp_get_sensor_delay() - gets sensor data acquisition period
19950dd64d5SKarol Wrona  * @data:	sensorhub structure
20050dd64d5SKarol Wrona  * @type:	SSP sensor type
20150dd64d5SKarol Wrona  *
20250dd64d5SKarol Wrona  * Returns acquisition period in ms
20350dd64d5SKarol Wrona  */
ssp_get_sensor_delay(struct ssp_data * data,enum ssp_sensor_type type)20450dd64d5SKarol Wrona u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type)
20550dd64d5SKarol Wrona {
20650dd64d5SKarol Wrona 	return data->delay_buf[type];
20750dd64d5SKarol Wrona }
2080a1b56b7SJonathan Cameron EXPORT_SYMBOL_NS(ssp_get_sensor_delay, IIO_SSP_SENSORS);
20950dd64d5SKarol Wrona 
21050dd64d5SKarol Wrona /**
21150dd64d5SKarol Wrona  * ssp_enable_sensor() - enables data acquisition for sensor
21250dd64d5SKarol Wrona  * @data:	sensorhub structure
21350dd64d5SKarol Wrona  * @type:	SSP sensor type
21450dd64d5SKarol Wrona  * @delay:	delay in ms
21550dd64d5SKarol Wrona  *
21650dd64d5SKarol Wrona  * Returns 0 or negative value in case of error
21750dd64d5SKarol Wrona  */
ssp_enable_sensor(struct ssp_data * data,enum ssp_sensor_type type,u32 delay)21850dd64d5SKarol Wrona int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
21950dd64d5SKarol Wrona 		      u32 delay)
22050dd64d5SKarol Wrona {
22150dd64d5SKarol Wrona 	int ret;
22250dd64d5SKarol Wrona 	struct ssp_instruction to_send;
22350dd64d5SKarol Wrona 
22450dd64d5SKarol Wrona 	to_send.a = cpu_to_le32(delay);
22550dd64d5SKarol Wrona 	to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
22650dd64d5SKarol Wrona 	to_send.c = data->batch_opt_buf[type];
22750dd64d5SKarol Wrona 
22850dd64d5SKarol Wrona 	switch (data->check_status[type]) {
22950dd64d5SKarol Wrona 	case SSP_INITIALIZATION_STATE:
23050dd64d5SKarol Wrona 		/* do calibration step, now just enable */
23150dd64d5SKarol Wrona 	case SSP_ADD_SENSOR_STATE:
23250dd64d5SKarol Wrona 		ret = ssp_send_instruction(data,
23350dd64d5SKarol Wrona 					   SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD,
23450dd64d5SKarol Wrona 					   type,
23550dd64d5SKarol Wrona 					   (u8 *)&to_send, sizeof(to_send));
23650dd64d5SKarol Wrona 		if (ret < 0) {
23750dd64d5SKarol Wrona 			dev_err(&data->spi->dev, "Enabling sensor failed\n");
23850dd64d5SKarol Wrona 			data->check_status[type] = SSP_NO_SENSOR_STATE;
23950dd64d5SKarol Wrona 			goto derror;
24050dd64d5SKarol Wrona 		}
24150dd64d5SKarol Wrona 
24250dd64d5SKarol Wrona 		data->sensor_enable |= BIT(type);
24350dd64d5SKarol Wrona 		data->check_status[type] = SSP_RUNNING_SENSOR_STATE;
24450dd64d5SKarol Wrona 		break;
24550dd64d5SKarol Wrona 	case SSP_RUNNING_SENSOR_STATE:
24650dd64d5SKarol Wrona 		ret = ssp_send_instruction(data,
24750dd64d5SKarol Wrona 					   SSP_MSG2SSP_INST_CHANGE_DELAY, type,
24850dd64d5SKarol Wrona 					   (u8 *)&to_send, sizeof(to_send));
24950dd64d5SKarol Wrona 		if (ret < 0) {
25050dd64d5SKarol Wrona 			dev_err(&data->spi->dev,
25150dd64d5SKarol Wrona 				"Changing sensor delay failed\n");
25250dd64d5SKarol Wrona 			goto derror;
25350dd64d5SKarol Wrona 		}
25450dd64d5SKarol Wrona 		break;
25550dd64d5SKarol Wrona 	default:
25650dd64d5SKarol Wrona 		data->check_status[type] = SSP_ADD_SENSOR_STATE;
25750dd64d5SKarol Wrona 		break;
25850dd64d5SKarol Wrona 	}
25950dd64d5SKarol Wrona 
26050dd64d5SKarol Wrona 	data->delay_buf[type] = delay;
26150dd64d5SKarol Wrona 
26250dd64d5SKarol Wrona 	if (atomic_inc_return(&data->enable_refcount) == 1)
26350dd64d5SKarol Wrona 		ssp_enable_wdt_timer(data);
26450dd64d5SKarol Wrona 
26550dd64d5SKarol Wrona 	return 0;
26650dd64d5SKarol Wrona 
26750dd64d5SKarol Wrona derror:
26850dd64d5SKarol Wrona 	return ret;
26950dd64d5SKarol Wrona }
2700a1b56b7SJonathan Cameron EXPORT_SYMBOL_NS(ssp_enable_sensor, IIO_SSP_SENSORS);
27150dd64d5SKarol Wrona 
27250dd64d5SKarol Wrona /**
27350dd64d5SKarol Wrona  * ssp_change_delay() - changes data acquisition for sensor
27450dd64d5SKarol Wrona  * @data:	sensorhub structure
27550dd64d5SKarol Wrona  * @type:	SSP sensor type
27650dd64d5SKarol Wrona  * @delay:	delay in ms
27750dd64d5SKarol Wrona  *
27850dd64d5SKarol Wrona  * Returns 0 or negative value in case of error
27950dd64d5SKarol Wrona  */
ssp_change_delay(struct ssp_data * data,enum ssp_sensor_type type,u32 delay)28050dd64d5SKarol Wrona int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
28150dd64d5SKarol Wrona 		     u32 delay)
28250dd64d5SKarol Wrona {
28350dd64d5SKarol Wrona 	int ret;
28450dd64d5SKarol Wrona 	struct ssp_instruction to_send;
28550dd64d5SKarol Wrona 
28650dd64d5SKarol Wrona 	to_send.a = cpu_to_le32(delay);
28750dd64d5SKarol Wrona 	to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
28850dd64d5SKarol Wrona 	to_send.c = data->batch_opt_buf[type];
28950dd64d5SKarol Wrona 
29050dd64d5SKarol Wrona 	ret = ssp_send_instruction(data, SSP_MSG2SSP_INST_CHANGE_DELAY, type,
29150dd64d5SKarol Wrona 				   (u8 *)&to_send, sizeof(to_send));
29250dd64d5SKarol Wrona 	if (ret < 0) {
29350dd64d5SKarol Wrona 		dev_err(&data->spi->dev, "Changing sensor delay failed\n");
29450dd64d5SKarol Wrona 		return ret;
29550dd64d5SKarol Wrona 	}
29650dd64d5SKarol Wrona 
29750dd64d5SKarol Wrona 	data->delay_buf[type] = delay;
29850dd64d5SKarol Wrona 
29950dd64d5SKarol Wrona 	return 0;
30050dd64d5SKarol Wrona }
3010a1b56b7SJonathan Cameron EXPORT_SYMBOL_NS(ssp_change_delay, IIO_SSP_SENSORS);
30250dd64d5SKarol Wrona 
30350dd64d5SKarol Wrona /**
30450dd64d5SKarol Wrona  * ssp_disable_sensor() - disables sensor
30550dd64d5SKarol Wrona  *
30650dd64d5SKarol Wrona  * @data:	sensorhub structure
30750dd64d5SKarol Wrona  * @type:	SSP sensor type
30850dd64d5SKarol Wrona  *
30950dd64d5SKarol Wrona  * Returns 0 or negative value in case of error
31050dd64d5SKarol Wrona  */
ssp_disable_sensor(struct ssp_data * data,enum ssp_sensor_type type)31150dd64d5SKarol Wrona int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
31250dd64d5SKarol Wrona {
31350dd64d5SKarol Wrona 	int ret;
31450dd64d5SKarol Wrona 	__le32 command;
31550dd64d5SKarol Wrona 
31650dd64d5SKarol Wrona 	if (data->sensor_enable & BIT(type)) {
31750dd64d5SKarol Wrona 		command = cpu_to_le32(data->delay_buf[type]);
31850dd64d5SKarol Wrona 
31950dd64d5SKarol Wrona 		ret = ssp_send_instruction(data,
32050dd64d5SKarol Wrona 					   SSP_MSG2SSP_INST_BYPASS_SENSOR_RM,
32150dd64d5SKarol Wrona 					   type, (u8 *)&command,
32250dd64d5SKarol Wrona 					   sizeof(command));
32350dd64d5SKarol Wrona 		if (ret < 0) {
32450dd64d5SKarol Wrona 			dev_err(&data->spi->dev, "Remove sensor fail\n");
32550dd64d5SKarol Wrona 			return ret;
32650dd64d5SKarol Wrona 		}
32750dd64d5SKarol Wrona 
32850dd64d5SKarol Wrona 		data->sensor_enable &= ~BIT(type);
32950dd64d5SKarol Wrona 	}
33050dd64d5SKarol Wrona 
33150dd64d5SKarol Wrona 	data->check_status[type] = SSP_ADD_SENSOR_STATE;
33250dd64d5SKarol Wrona 
33350dd64d5SKarol Wrona 	if (atomic_dec_and_test(&data->enable_refcount))
33450dd64d5SKarol Wrona 		ssp_disable_wdt_timer(data);
33550dd64d5SKarol Wrona 
33650dd64d5SKarol Wrona 	return 0;
33750dd64d5SKarol Wrona }
3380a1b56b7SJonathan Cameron EXPORT_SYMBOL_NS(ssp_disable_sensor, IIO_SSP_SENSORS);
33950dd64d5SKarol Wrona 
ssp_irq_thread_fn(int irq,void * dev_id)34050dd64d5SKarol Wrona static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id)
34150dd64d5SKarol Wrona {
34250dd64d5SKarol Wrona 	struct ssp_data *data = dev_id;
34350dd64d5SKarol Wrona 
34450dd64d5SKarol Wrona 	/*
34550dd64d5SKarol Wrona 	 * This wrapper is done to preserve error path for ssp_irq_msg, also
34650dd64d5SKarol Wrona 	 * it is defined in different file.
34750dd64d5SKarol Wrona 	 */
34850dd64d5SKarol Wrona 	ssp_irq_msg(data);
34950dd64d5SKarol Wrona 
35050dd64d5SKarol Wrona 	return IRQ_HANDLED;
35150dd64d5SKarol Wrona }
35250dd64d5SKarol Wrona 
ssp_initialize_mcu(struct ssp_data * data)35350dd64d5SKarol Wrona static int ssp_initialize_mcu(struct ssp_data *data)
35450dd64d5SKarol Wrona {
35550dd64d5SKarol Wrona 	int ret;
35650dd64d5SKarol Wrona 
35750dd64d5SKarol Wrona 	ssp_clean_pending_list(data);
35850dd64d5SKarol Wrona 
35950dd64d5SKarol Wrona 	ret = ssp_get_chipid(data);
36050dd64d5SKarol Wrona 	if (ret != SSP_DEVICE_ID) {
36150dd64d5SKarol Wrona 		dev_err(&data->spi->dev, "%s - MCU %s ret = %d\n", __func__,
36250dd64d5SKarol Wrona 			ret < 0 ? "is not working" : "identification failed",
36350dd64d5SKarol Wrona 			ret);
36450dd64d5SKarol Wrona 		return ret < 0 ? ret : -ENODEV;
36550dd64d5SKarol Wrona 	}
36650dd64d5SKarol Wrona 
36750dd64d5SKarol Wrona 	dev_info(&data->spi->dev, "MCU device ID = %d\n", ret);
36850dd64d5SKarol Wrona 
36950dd64d5SKarol Wrona 	/*
37050dd64d5SKarol Wrona 	 * needs clarification, for now do not want to export all transfer
37150dd64d5SKarol Wrona 	 * methods to sensors' drivers
37250dd64d5SKarol Wrona 	 */
37350dd64d5SKarol Wrona 	ret = ssp_set_magnetic_matrix(data);
37450dd64d5SKarol Wrona 	if (ret < 0) {
37550dd64d5SKarol Wrona 		dev_err(&data->spi->dev,
37650dd64d5SKarol Wrona 			"%s - ssp_set_magnetic_matrix failed\n", __func__);
37750dd64d5SKarol Wrona 		return ret;
37850dd64d5SKarol Wrona 	}
37950dd64d5SKarol Wrona 
38050dd64d5SKarol Wrona 	data->available_sensors = ssp_get_sensor_scanning_info(data);
38150dd64d5SKarol Wrona 	if (data->available_sensors == 0) {
38250dd64d5SKarol Wrona 		dev_err(&data->spi->dev,
38350dd64d5SKarol Wrona 			"%s - ssp_get_sensor_scanning_info failed\n", __func__);
38450dd64d5SKarol Wrona 		return -EIO;
38550dd64d5SKarol Wrona 	}
38650dd64d5SKarol Wrona 
38750dd64d5SKarol Wrona 	data->cur_firm_rev = ssp_get_firmware_rev(data);
38850dd64d5SKarol Wrona 	dev_info(&data->spi->dev, "MCU Firm Rev : New = %8u\n",
38950dd64d5SKarol Wrona 		 data->cur_firm_rev);
39050dd64d5SKarol Wrona 
39150dd64d5SKarol Wrona 	return ssp_command(data, SSP_MSG2SSP_AP_MCU_DUMP_CHECK, 0);
39250dd64d5SKarol Wrona }
39350dd64d5SKarol Wrona 
39450dd64d5SKarol Wrona /*
39550dd64d5SKarol Wrona  * sensorhub can request its reinitialization as some brutal and rare error
39650dd64d5SKarol Wrona  * handling. It can be requested from the MCU.
39750dd64d5SKarol Wrona  */
ssp_refresh_task(struct work_struct * work)39850dd64d5SKarol Wrona static void ssp_refresh_task(struct work_struct *work)
39950dd64d5SKarol Wrona {
40050dd64d5SKarol Wrona 	struct ssp_data *data = container_of((struct delayed_work *)work,
40150dd64d5SKarol Wrona 					     struct ssp_data, work_refresh);
40250dd64d5SKarol Wrona 
40350dd64d5SKarol Wrona 	dev_info(&data->spi->dev, "refreshing\n");
40450dd64d5SKarol Wrona 
40550dd64d5SKarol Wrona 	data->reset_cnt++;
40650dd64d5SKarol Wrona 
40750dd64d5SKarol Wrona 	if (ssp_initialize_mcu(data) >= 0) {
40850dd64d5SKarol Wrona 		ssp_sync_available_sensors(data);
40950dd64d5SKarol Wrona 		if (data->last_ap_state != 0)
41050dd64d5SKarol Wrona 			ssp_command(data, data->last_ap_state, 0);
41150dd64d5SKarol Wrona 
41250dd64d5SKarol Wrona 		if (data->last_resume_state != 0)
41350dd64d5SKarol Wrona 			ssp_command(data, data->last_resume_state, 0);
41450dd64d5SKarol Wrona 
41550dd64d5SKarol Wrona 		data->timeout_cnt = 0;
41650dd64d5SKarol Wrona 		data->com_fail_cnt = 0;
41750dd64d5SKarol Wrona 	}
41850dd64d5SKarol Wrona }
41950dd64d5SKarol Wrona 
ssp_queue_ssp_refresh_task(struct ssp_data * data,unsigned int delay)42050dd64d5SKarol Wrona int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
42150dd64d5SKarol Wrona {
42250dd64d5SKarol Wrona 	cancel_delayed_work_sync(&data->work_refresh);
42350dd64d5SKarol Wrona 
42450dd64d5SKarol Wrona 	return queue_delayed_work(system_power_efficient_wq,
42550dd64d5SKarol Wrona 				  &data->work_refresh,
42650dd64d5SKarol Wrona 				  msecs_to_jiffies(delay));
42750dd64d5SKarol Wrona }
42850dd64d5SKarol Wrona 
4297253606dSFabian Frederick static const struct of_device_id ssp_of_match[] = {
43050dd64d5SKarol Wrona 	{
43150dd64d5SKarol Wrona 		.compatible	= "samsung,sensorhub-rinato",
43250dd64d5SKarol Wrona 		.data		= &ssp_rinato_info,
43350dd64d5SKarol Wrona 	}, {
43450dd64d5SKarol Wrona 		.compatible	= "samsung,sensorhub-thermostat",
43550dd64d5SKarol Wrona 		.data		= &ssp_thermostat_info,
43650dd64d5SKarol Wrona 	},
43750dd64d5SKarol Wrona 	{},
43850dd64d5SKarol Wrona };
43950dd64d5SKarol Wrona MODULE_DEVICE_TABLE(of, ssp_of_match);
44050dd64d5SKarol Wrona 
ssp_parse_dt(struct device * dev)44150dd64d5SKarol Wrona static struct ssp_data *ssp_parse_dt(struct device *dev)
44250dd64d5SKarol Wrona {
44350dd64d5SKarol Wrona 	struct ssp_data *data;
44450dd64d5SKarol Wrona 
44550dd64d5SKarol Wrona 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
44650dd64d5SKarol Wrona 	if (!data)
44750dd64d5SKarol Wrona 		return NULL;
44850dd64d5SKarol Wrona 
4494cf01d6dSLinus Walleij 	data->mcu_ap_gpiod = devm_gpiod_get(dev, "mcu-ap", GPIOD_IN);
4504cf01d6dSLinus Walleij 	if (IS_ERR(data->mcu_ap_gpiod))
451388008bbSUwe Kleine-König 		return NULL;
45250dd64d5SKarol Wrona 
4534cf01d6dSLinus Walleij 	data->ap_mcu_gpiod = devm_gpiod_get(dev, "ap-mcu", GPIOD_OUT_HIGH);
4544cf01d6dSLinus Walleij 	if (IS_ERR(data->ap_mcu_gpiod))
455388008bbSUwe Kleine-König 		return NULL;
45650dd64d5SKarol Wrona 
4574cf01d6dSLinus Walleij 	data->mcu_reset_gpiod = devm_gpiod_get(dev, "mcu-reset",
4584cf01d6dSLinus Walleij 					       GPIOD_OUT_HIGH);
4594cf01d6dSLinus Walleij 	if (IS_ERR(data->mcu_reset_gpiod))
460388008bbSUwe Kleine-König 		return NULL;
46150dd64d5SKarol Wrona 
462b946e949SAndy Shevchenko 	data->sensorhub_info = device_get_match_data(dev);
46350dd64d5SKarol Wrona 
46450dd64d5SKarol Wrona 	dev_set_drvdata(dev, data);
46550dd64d5SKarol Wrona 
46650dd64d5SKarol Wrona 	return data;
46750dd64d5SKarol Wrona }
46850dd64d5SKarol Wrona 
46950dd64d5SKarol Wrona /**
47050dd64d5SKarol Wrona  * ssp_register_consumer() - registers iio consumer in ssp framework
47150dd64d5SKarol Wrona  *
47250dd64d5SKarol Wrona  * @indio_dev:	consumer iio device
47350dd64d5SKarol Wrona  * @type:	ssp sensor type
47450dd64d5SKarol Wrona  */
ssp_register_consumer(struct iio_dev * indio_dev,enum ssp_sensor_type type)47550dd64d5SKarol Wrona void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type type)
47650dd64d5SKarol Wrona {
47750dd64d5SKarol Wrona 	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
47850dd64d5SKarol Wrona 
47950dd64d5SKarol Wrona 	data->sensor_devs[type] = indio_dev;
48050dd64d5SKarol Wrona }
4810a1b56b7SJonathan Cameron EXPORT_SYMBOL_NS(ssp_register_consumer, IIO_SSP_SENSORS);
48250dd64d5SKarol Wrona 
ssp_probe(struct spi_device * spi)48350dd64d5SKarol Wrona static int ssp_probe(struct spi_device *spi)
48450dd64d5SKarol Wrona {
48550dd64d5SKarol Wrona 	int ret, i;
48650dd64d5SKarol Wrona 	struct ssp_data *data;
48750dd64d5SKarol Wrona 
48850dd64d5SKarol Wrona 	data = ssp_parse_dt(&spi->dev);
48950dd64d5SKarol Wrona 	if (!data) {
49050dd64d5SKarol Wrona 		dev_err(&spi->dev, "Failed to find platform data\n");
49150dd64d5SKarol Wrona 		return -ENODEV;
49250dd64d5SKarol Wrona 	}
49350dd64d5SKarol Wrona 
4944c6e3dbcSKrzysztof Kozlowski 	ret = mfd_add_devices(&spi->dev, PLATFORM_DEVID_NONE,
4954c6e3dbcSKrzysztof Kozlowski 			      sensorhub_sensor_devs,
49650dd64d5SKarol Wrona 			      ARRAY_SIZE(sensorhub_sensor_devs), NULL, 0, NULL);
49750dd64d5SKarol Wrona 	if (ret < 0) {
49850dd64d5SKarol Wrona 		dev_err(&spi->dev, "mfd add devices fail\n");
49950dd64d5SKarol Wrona 		return ret;
50050dd64d5SKarol Wrona 	}
50150dd64d5SKarol Wrona 
50250dd64d5SKarol Wrona 	spi->mode = SPI_MODE_1;
50350dd64d5SKarol Wrona 	ret = spi_setup(spi);
50450dd64d5SKarol Wrona 	if (ret < 0) {
50550dd64d5SKarol Wrona 		dev_err(&spi->dev, "Failed to setup spi\n");
50650dd64d5SKarol Wrona 		return ret;
50750dd64d5SKarol Wrona 	}
50850dd64d5SKarol Wrona 
50950dd64d5SKarol Wrona 	data->fw_dl_state = SSP_FW_DL_STATE_NONE;
51050dd64d5SKarol Wrona 	data->spi = spi;
51150dd64d5SKarol Wrona 	spi_set_drvdata(spi, data);
51250dd64d5SKarol Wrona 
51350dd64d5SKarol Wrona 	mutex_init(&data->comm_lock);
51450dd64d5SKarol Wrona 
51550dd64d5SKarol Wrona 	for (i = 0; i < SSP_SENSOR_MAX; ++i) {
51650dd64d5SKarol Wrona 		data->delay_buf[i] = SSP_DEFAULT_POLLING_DELAY;
51750dd64d5SKarol Wrona 		data->batch_latency_buf[i] = 0;
51850dd64d5SKarol Wrona 		data->batch_opt_buf[i] = 0;
51950dd64d5SKarol Wrona 		data->check_status[i] = SSP_INITIALIZATION_STATE;
52050dd64d5SKarol Wrona 	}
52150dd64d5SKarol Wrona 
52250dd64d5SKarol Wrona 	data->delay_buf[SSP_BIO_HRM_LIB] = 100;
52350dd64d5SKarol Wrona 
52450dd64d5SKarol Wrona 	data->time_syncing = true;
52550dd64d5SKarol Wrona 
52650dd64d5SKarol Wrona 	mutex_init(&data->pending_lock);
52750dd64d5SKarol Wrona 	INIT_LIST_HEAD(&data->pending_list);
52850dd64d5SKarol Wrona 
52950dd64d5SKarol Wrona 	atomic_set(&data->enable_refcount, 0);
53050dd64d5SKarol Wrona 
53150dd64d5SKarol Wrona 	INIT_WORK(&data->work_wdt, ssp_wdt_work_func);
53250dd64d5SKarol Wrona 	INIT_DELAYED_WORK(&data->work_refresh, ssp_refresh_task);
53350dd64d5SKarol Wrona 
534e99e88a9SKees Cook 	timer_setup(&data->wdt_timer, ssp_wdt_timer_func, 0);
53550dd64d5SKarol Wrona 
53650dd64d5SKarol Wrona 	ret = request_threaded_irq(data->spi->irq, NULL,
53750dd64d5SKarol Wrona 				   ssp_irq_thread_fn,
53850dd64d5SKarol Wrona 				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
53950dd64d5SKarol Wrona 				   "SSP_Int", data);
54050dd64d5SKarol Wrona 	if (ret < 0) {
54150dd64d5SKarol Wrona 		dev_err(&spi->dev, "Irq request fail\n");
54250dd64d5SKarol Wrona 		goto err_setup_irq;
54350dd64d5SKarol Wrona 	}
54450dd64d5SKarol Wrona 
54550dd64d5SKarol Wrona 	/* Let's start with enabled one so irq balance could be ok */
54650dd64d5SKarol Wrona 	data->shut_down = false;
54750dd64d5SKarol Wrona 
54850dd64d5SKarol Wrona 	/* just to avoid unbalanced irq set wake up */
54950dd64d5SKarol Wrona 	enable_irq_wake(data->spi->irq);
55050dd64d5SKarol Wrona 
55150dd64d5SKarol Wrona 	data->fw_dl_state = ssp_check_fwbl(data);
55250dd64d5SKarol Wrona 	if (data->fw_dl_state == SSP_FW_DL_STATE_NONE) {
55350dd64d5SKarol Wrona 		ret = ssp_initialize_mcu(data);
55450dd64d5SKarol Wrona 		if (ret < 0) {
55550dd64d5SKarol Wrona 			dev_err(&spi->dev, "Initialize_mcu failed\n");
55650dd64d5SKarol Wrona 			goto err_read_reg;
55750dd64d5SKarol Wrona 		}
55850dd64d5SKarol Wrona 	} else {
55950dd64d5SKarol Wrona 		dev_err(&spi->dev, "Firmware version not supported\n");
56050dd64d5SKarol Wrona 		ret = -EPERM;
56150dd64d5SKarol Wrona 		goto err_read_reg;
56250dd64d5SKarol Wrona 	}
56350dd64d5SKarol Wrona 
56450dd64d5SKarol Wrona 	return 0;
56550dd64d5SKarol Wrona 
56650dd64d5SKarol Wrona err_read_reg:
56750dd64d5SKarol Wrona 	free_irq(data->spi->irq, data);
56850dd64d5SKarol Wrona err_setup_irq:
56950dd64d5SKarol Wrona 	mutex_destroy(&data->pending_lock);
57050dd64d5SKarol Wrona 	mutex_destroy(&data->comm_lock);
57150dd64d5SKarol Wrona 
57250dd64d5SKarol Wrona 	dev_err(&spi->dev, "Probe failed!\n");
57350dd64d5SKarol Wrona 
57450dd64d5SKarol Wrona 	return ret;
57550dd64d5SKarol Wrona }
57650dd64d5SKarol Wrona 
ssp_remove(struct spi_device * spi)57750dd64d5SKarol Wrona static void ssp_remove(struct spi_device *spi)
57850dd64d5SKarol Wrona {
57950dd64d5SKarol Wrona 	struct ssp_data *data = spi_get_drvdata(spi);
58050dd64d5SKarol Wrona 
58150dd64d5SKarol Wrona 	if (ssp_command(data, SSP_MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)
58250dd64d5SKarol Wrona 		dev_err(&data->spi->dev,
58350dd64d5SKarol Wrona 			"SSP_MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
58450dd64d5SKarol Wrona 
58550dd64d5SKarol Wrona 	ssp_enable_mcu(data, false);
58650dd64d5SKarol Wrona 	ssp_disable_wdt_timer(data);
58750dd64d5SKarol Wrona 
58850dd64d5SKarol Wrona 	ssp_clean_pending_list(data);
58950dd64d5SKarol Wrona 
59050dd64d5SKarol Wrona 	free_irq(data->spi->irq, data);
59150dd64d5SKarol Wrona 
59250dd64d5SKarol Wrona 	del_timer_sync(&data->wdt_timer);
59350dd64d5SKarol Wrona 	cancel_work_sync(&data->work_wdt);
59450dd64d5SKarol Wrona 
59550dd64d5SKarol Wrona 	mutex_destroy(&data->comm_lock);
59650dd64d5SKarol Wrona 	mutex_destroy(&data->pending_lock);
59750dd64d5SKarol Wrona 
59850dd64d5SKarol Wrona 	mfd_remove_devices(&spi->dev);
59950dd64d5SKarol Wrona }
60050dd64d5SKarol Wrona 
ssp_suspend(struct device * dev)60150dd64d5SKarol Wrona static int ssp_suspend(struct device *dev)
60250dd64d5SKarol Wrona {
60350dd64d5SKarol Wrona 	int ret;
60450dd64d5SKarol Wrona 	struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
60550dd64d5SKarol Wrona 
60650dd64d5SKarol Wrona 	data->last_resume_state = SSP_MSG2SSP_AP_STATUS_SUSPEND;
60750dd64d5SKarol Wrona 
60850dd64d5SKarol Wrona 	if (atomic_read(&data->enable_refcount) > 0)
60950dd64d5SKarol Wrona 		ssp_disable_wdt_timer(data);
61050dd64d5SKarol Wrona 
61150dd64d5SKarol Wrona 	ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_SUSPEND, 0);
61250dd64d5SKarol Wrona 	if (ret < 0) {
61350dd64d5SKarol Wrona 		dev_err(&data->spi->dev,
61450dd64d5SKarol Wrona 			"%s SSP_MSG2SSP_AP_STATUS_SUSPEND failed\n", __func__);
61550dd64d5SKarol Wrona 
61650dd64d5SKarol Wrona 		ssp_enable_wdt_timer(data);
61750dd64d5SKarol Wrona 		return ret;
61850dd64d5SKarol Wrona 	}
61950dd64d5SKarol Wrona 
62050dd64d5SKarol Wrona 	data->time_syncing = false;
62150dd64d5SKarol Wrona 	disable_irq(data->spi->irq);
62250dd64d5SKarol Wrona 
62350dd64d5SKarol Wrona 	return 0;
62450dd64d5SKarol Wrona }
62550dd64d5SKarol Wrona 
ssp_resume(struct device * dev)62650dd64d5SKarol Wrona static int ssp_resume(struct device *dev)
62750dd64d5SKarol Wrona {
62850dd64d5SKarol Wrona 	int ret;
62950dd64d5SKarol Wrona 	struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
63050dd64d5SKarol Wrona 
63150dd64d5SKarol Wrona 	enable_irq(data->spi->irq);
63250dd64d5SKarol Wrona 
63350dd64d5SKarol Wrona 	if (atomic_read(&data->enable_refcount) > 0)
63450dd64d5SKarol Wrona 		ssp_enable_wdt_timer(data);
63550dd64d5SKarol Wrona 
63650dd64d5SKarol Wrona 	ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_RESUME, 0);
63750dd64d5SKarol Wrona 	if (ret < 0) {
63850dd64d5SKarol Wrona 		dev_err(&data->spi->dev,
63950dd64d5SKarol Wrona 			"%s SSP_MSG2SSP_AP_STATUS_RESUME failed\n", __func__);
64050dd64d5SKarol Wrona 		ssp_disable_wdt_timer(data);
64150dd64d5SKarol Wrona 		return ret;
64250dd64d5SKarol Wrona 	}
64350dd64d5SKarol Wrona 
64450dd64d5SKarol Wrona 	/* timesyncing is set by MCU */
64550dd64d5SKarol Wrona 	data->last_resume_state = SSP_MSG2SSP_AP_STATUS_RESUME;
64650dd64d5SKarol Wrona 
64750dd64d5SKarol Wrona 	return 0;
64850dd64d5SKarol Wrona }
64950dd64d5SKarol Wrona 
65050dd64d5SKarol Wrona static DEFINE_SIMPLE_DEV_PM_OPS(ssp_pm_ops, ssp_suspend, ssp_resume);
65150dd64d5SKarol Wrona 
652bb08abc7SJonathan Cameron static struct spi_driver ssp_driver = {
65350dd64d5SKarol Wrona 	.probe = ssp_probe,
65450dd64d5SKarol Wrona 	.remove = ssp_remove,
65550dd64d5SKarol Wrona 	.driver = {
65650dd64d5SKarol Wrona 		.pm = pm_sleep_ptr(&ssp_pm_ops),
65750dd64d5SKarol Wrona 		.of_match_table = ssp_of_match,
658bb08abc7SJonathan Cameron 		.name = "sensorhub"
659b946e949SAndy Shevchenko 	},
66050dd64d5SKarol Wrona };
66150dd64d5SKarol Wrona 
66250dd64d5SKarol Wrona module_spi_driver(ssp_driver);
66350dd64d5SKarol Wrona 
66450dd64d5SKarol Wrona MODULE_DESCRIPTION("ssp sensorhub driver");
66550dd64d5SKarol Wrona MODULE_AUTHOR("Samsung Electronics");
66650dd64d5SKarol Wrona MODULE_LICENSE("GPL");
66750dd64d5SKarol Wrona