brintos

brintos / linux-shallow public Read only

0
0
Text · 15.8 KiB · e64d242 Raw
667 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.4 */5 6#include <linux/iio/iio.h>7#include <linux/interrupt.h>8#include <linux/io.h>9#include <linux/mfd/core.h>10#include <linux/mod_devicetable.h>11#include <linux/module.h>12#include <linux/property.h>13 14#include "ssp.h"15 16#define SSP_WDT_TIME			1000017#define SSP_LIMIT_RESET_CNT		2018#define SSP_LIMIT_TIMEOUT_CNT		319 20/* It is possible that it is max clk rate for version 1.0 of bootcode */21#define SSP_BOOT_SPI_HZ	40000022 23/*24 * These fields can look enigmatic but this structure is used mainly to flat25 * some values and depends on command type.26 */27struct ssp_instruction {28	__le32 a;29	__le32 b;30	u8 c;31} __attribute__((__packed__));32 33static const u8 ssp_magnitude_table[] = {110, 85, 171, 71, 203, 195, 0, 67,34	208, 56, 175, 244, 206, 213, 0, 92, 250, 0, 55, 48, 189, 252, 171,35	243, 13, 45, 250};36 37static const struct ssp_sensorhub_info ssp_rinato_info = {38	.fw_name = "ssp_B2.fw",39	.fw_crashed_name = "ssp_crashed.fw",40	.fw_rev = 14052300,41	.mag_table = ssp_magnitude_table,42	.mag_length = ARRAY_SIZE(ssp_magnitude_table),43};44 45static const struct ssp_sensorhub_info ssp_thermostat_info = {46	.fw_name = "thermostat_B2.fw",47	.fw_crashed_name = "ssp_crashed.fw",48	.fw_rev = 14080600,49	.mag_table = ssp_magnitude_table,50	.mag_length = ARRAY_SIZE(ssp_magnitude_table),51};52 53static const struct mfd_cell sensorhub_sensor_devs[] = {54	{55		.name = "ssp-accelerometer",56	},57	{58		.name = "ssp-gyroscope",59	},60};61 62static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data)63{64	gpiod_set_value(data->mcu_reset_gpiod, 0);65	usleep_range(1000, 1200);66	gpiod_set_value(data->mcu_reset_gpiod, 1);67	msleep(50);68}69 70static void ssp_sync_available_sensors(struct ssp_data *data)71{72	int i, ret;73 74	for (i = 0; i < SSP_SENSOR_MAX; ++i) {75		if (data->available_sensors & BIT(i)) {76			ret = ssp_enable_sensor(data, i, data->delay_buf[i]);77			if (ret < 0) {78				dev_err(&data->spi->dev,79					"Sync sensor nr: %d fail\n", i);80				continue;81			}82		}83	}84 85	ret = ssp_command(data, SSP_MSG2SSP_AP_MCU_SET_DUMPMODE,86			  data->mcu_dump_mode);87	if (ret < 0)88		dev_err(&data->spi->dev,89			"SSP_MSG2SSP_AP_MCU_SET_DUMPMODE failed\n");90}91 92static void ssp_enable_mcu(struct ssp_data *data, bool enable)93{94	dev_info(&data->spi->dev, "current shutdown = %d, old = %d\n", enable,95		 data->shut_down);96 97	if (enable && data->shut_down) {98		data->shut_down = false;99		enable_irq(data->spi->irq);100		enable_irq_wake(data->spi->irq);101	} else if (!enable && !data->shut_down) {102		data->shut_down = true;103		disable_irq(data->spi->irq);104		disable_irq_wake(data->spi->irq);105	} else {106		dev_warn(&data->spi->dev, "current shutdown = %d, old = %d\n",107			 enable, data->shut_down);108	}109}110 111/*112 * This function is the first one which communicates with the mcu so it is113 * possible that the first attempt will fail114 */115static int ssp_check_fwbl(struct ssp_data *data)116{117	int retries = 0;118 119	while (retries++ < 5) {120		data->cur_firm_rev = ssp_get_firmware_rev(data);121		if (data->cur_firm_rev == SSP_INVALID_REVISION ||122		    data->cur_firm_rev == SSP_INVALID_REVISION2) {123			dev_warn(&data->spi->dev,124				 "Invalid revision, trying %d time\n", retries);125		} else {126			break;127		}128	}129 130	if (data->cur_firm_rev == SSP_INVALID_REVISION ||131	    data->cur_firm_rev == SSP_INVALID_REVISION2) {132		dev_err(&data->spi->dev, "SSP_INVALID_REVISION\n");133		return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;134	}135 136	dev_info(&data->spi->dev,137		 "MCU Firm Rev : Old = %8u, New = %8u\n",138		 data->cur_firm_rev,139		 data->sensorhub_info->fw_rev);140 141	if (data->cur_firm_rev != data->sensorhub_info->fw_rev)142		return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;143 144	return SSP_FW_DL_STATE_NONE;145}146 147static void ssp_reset_mcu(struct ssp_data *data)148{149	ssp_enable_mcu(data, false);150	ssp_clean_pending_list(data);151	ssp_toggle_mcu_reset_gpio(data);152	ssp_enable_mcu(data, true);153}154 155static void ssp_wdt_work_func(struct work_struct *work)156{157	struct ssp_data *data = container_of(work, struct ssp_data, work_wdt);158 159	dev_err(&data->spi->dev, "%s - Sensor state: 0x%x, RC: %u, CC: %u\n",160		__func__, data->available_sensors, data->reset_cnt,161		data->com_fail_cnt);162 163	ssp_reset_mcu(data);164	data->com_fail_cnt = 0;165	data->timeout_cnt = 0;166}167 168static void ssp_wdt_timer_func(struct timer_list *t)169{170	struct ssp_data *data = from_timer(data, t, wdt_timer);171 172	switch (data->fw_dl_state) {173	case SSP_FW_DL_STATE_FAIL:174	case SSP_FW_DL_STATE_DOWNLOADING:175	case SSP_FW_DL_STATE_SYNC:176		goto _mod;177	}178 179	if (data->timeout_cnt > SSP_LIMIT_TIMEOUT_CNT ||180	    data->com_fail_cnt > SSP_LIMIT_RESET_CNT)181		queue_work(system_power_efficient_wq, &data->work_wdt);182_mod:183	mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));184}185 186static void ssp_enable_wdt_timer(struct ssp_data *data)187{188	mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));189}190 191static void ssp_disable_wdt_timer(struct ssp_data *data)192{193	del_timer_sync(&data->wdt_timer);194	cancel_work_sync(&data->work_wdt);195}196 197/**198 * ssp_get_sensor_delay() - gets sensor data acquisition period199 * @data:	sensorhub structure200 * @type:	SSP sensor type201 *202 * Returns acquisition period in ms203 */204u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type)205{206	return data->delay_buf[type];207}208EXPORT_SYMBOL_NS(ssp_get_sensor_delay, IIO_SSP_SENSORS);209 210/**211 * ssp_enable_sensor() - enables data acquisition for sensor212 * @data:	sensorhub structure213 * @type:	SSP sensor type214 * @delay:	delay in ms215 *216 * Returns 0 or negative value in case of error217 */218int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,219		      u32 delay)220{221	int ret;222	struct ssp_instruction to_send;223 224	to_send.a = cpu_to_le32(delay);225	to_send.b = cpu_to_le32(data->batch_latency_buf[type]);226	to_send.c = data->batch_opt_buf[type];227 228	switch (data->check_status[type]) {229	case SSP_INITIALIZATION_STATE:230		/* do calibration step, now just enable */231	case SSP_ADD_SENSOR_STATE:232		ret = ssp_send_instruction(data,233					   SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD,234					   type,235					   (u8 *)&to_send, sizeof(to_send));236		if (ret < 0) {237			dev_err(&data->spi->dev, "Enabling sensor failed\n");238			data->check_status[type] = SSP_NO_SENSOR_STATE;239			goto derror;240		}241 242		data->sensor_enable |= BIT(type);243		data->check_status[type] = SSP_RUNNING_SENSOR_STATE;244		break;245	case SSP_RUNNING_SENSOR_STATE:246		ret = ssp_send_instruction(data,247					   SSP_MSG2SSP_INST_CHANGE_DELAY, type,248					   (u8 *)&to_send, sizeof(to_send));249		if (ret < 0) {250			dev_err(&data->spi->dev,251				"Changing sensor delay failed\n");252			goto derror;253		}254		break;255	default:256		data->check_status[type] = SSP_ADD_SENSOR_STATE;257		break;258	}259 260	data->delay_buf[type] = delay;261 262	if (atomic_inc_return(&data->enable_refcount) == 1)263		ssp_enable_wdt_timer(data);264 265	return 0;266 267derror:268	return ret;269}270EXPORT_SYMBOL_NS(ssp_enable_sensor, IIO_SSP_SENSORS);271 272/**273 * ssp_change_delay() - changes data acquisition for sensor274 * @data:	sensorhub structure275 * @type:	SSP sensor type276 * @delay:	delay in ms277 *278 * Returns 0 or negative value in case of error279 */280int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,281		     u32 delay)282{283	int ret;284	struct ssp_instruction to_send;285 286	to_send.a = cpu_to_le32(delay);287	to_send.b = cpu_to_le32(data->batch_latency_buf[type]);288	to_send.c = data->batch_opt_buf[type];289 290	ret = ssp_send_instruction(data, SSP_MSG2SSP_INST_CHANGE_DELAY, type,291				   (u8 *)&to_send, sizeof(to_send));292	if (ret < 0) {293		dev_err(&data->spi->dev, "Changing sensor delay failed\n");294		return ret;295	}296 297	data->delay_buf[type] = delay;298 299	return 0;300}301EXPORT_SYMBOL_NS(ssp_change_delay, IIO_SSP_SENSORS);302 303/**304 * ssp_disable_sensor() - disables sensor305 *306 * @data:	sensorhub structure307 * @type:	SSP sensor type308 *309 * Returns 0 or negative value in case of error310 */311int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)312{313	int ret;314	__le32 command;315 316	if (data->sensor_enable & BIT(type)) {317		command = cpu_to_le32(data->delay_buf[type]);318 319		ret = ssp_send_instruction(data,320					   SSP_MSG2SSP_INST_BYPASS_SENSOR_RM,321					   type, (u8 *)&command,322					   sizeof(command));323		if (ret < 0) {324			dev_err(&data->spi->dev, "Remove sensor fail\n");325			return ret;326		}327 328		data->sensor_enable &= ~BIT(type);329	}330 331	data->check_status[type] = SSP_ADD_SENSOR_STATE;332 333	if (atomic_dec_and_test(&data->enable_refcount))334		ssp_disable_wdt_timer(data);335 336	return 0;337}338EXPORT_SYMBOL_NS(ssp_disable_sensor, IIO_SSP_SENSORS);339 340static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id)341{342	struct ssp_data *data = dev_id;343 344	/*345	 * This wrapper is done to preserve error path for ssp_irq_msg, also346	 * it is defined in different file.347	 */348	ssp_irq_msg(data);349 350	return IRQ_HANDLED;351}352 353static int ssp_initialize_mcu(struct ssp_data *data)354{355	int ret;356 357	ssp_clean_pending_list(data);358 359	ret = ssp_get_chipid(data);360	if (ret != SSP_DEVICE_ID) {361		dev_err(&data->spi->dev, "%s - MCU %s ret = %d\n", __func__,362			ret < 0 ? "is not working" : "identification failed",363			ret);364		return ret < 0 ? ret : -ENODEV;365	}366 367	dev_info(&data->spi->dev, "MCU device ID = %d\n", ret);368 369	/*370	 * needs clarification, for now do not want to export all transfer371	 * methods to sensors' drivers372	 */373	ret = ssp_set_magnetic_matrix(data);374	if (ret < 0) {375		dev_err(&data->spi->dev,376			"%s - ssp_set_magnetic_matrix failed\n", __func__);377		return ret;378	}379 380	data->available_sensors = ssp_get_sensor_scanning_info(data);381	if (data->available_sensors == 0) {382		dev_err(&data->spi->dev,383			"%s - ssp_get_sensor_scanning_info failed\n", __func__);384		return -EIO;385	}386 387	data->cur_firm_rev = ssp_get_firmware_rev(data);388	dev_info(&data->spi->dev, "MCU Firm Rev : New = %8u\n",389		 data->cur_firm_rev);390 391	return ssp_command(data, SSP_MSG2SSP_AP_MCU_DUMP_CHECK, 0);392}393 394/*395 * sensorhub can request its reinitialization as some brutal and rare error396 * handling. It can be requested from the MCU.397 */398static void ssp_refresh_task(struct work_struct *work)399{400	struct ssp_data *data = container_of((struct delayed_work *)work,401					     struct ssp_data, work_refresh);402 403	dev_info(&data->spi->dev, "refreshing\n");404 405	data->reset_cnt++;406 407	if (ssp_initialize_mcu(data) >= 0) {408		ssp_sync_available_sensors(data);409		if (data->last_ap_state != 0)410			ssp_command(data, data->last_ap_state, 0);411 412		if (data->last_resume_state != 0)413			ssp_command(data, data->last_resume_state, 0);414 415		data->timeout_cnt = 0;416		data->com_fail_cnt = 0;417	}418}419 420int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)421{422	cancel_delayed_work_sync(&data->work_refresh);423 424	return queue_delayed_work(system_power_efficient_wq,425				  &data->work_refresh,426				  msecs_to_jiffies(delay));427}428 429static const struct of_device_id ssp_of_match[] = {430	{431		.compatible	= "samsung,sensorhub-rinato",432		.data		= &ssp_rinato_info,433	}, {434		.compatible	= "samsung,sensorhub-thermostat",435		.data		= &ssp_thermostat_info,436	},437	{},438};439MODULE_DEVICE_TABLE(of, ssp_of_match);440 441static struct ssp_data *ssp_parse_dt(struct device *dev)442{443	struct ssp_data *data;444 445	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);446	if (!data)447		return NULL;448 449	data->mcu_ap_gpiod = devm_gpiod_get(dev, "mcu-ap", GPIOD_IN);450	if (IS_ERR(data->mcu_ap_gpiod))451		return NULL;452 453	data->ap_mcu_gpiod = devm_gpiod_get(dev, "ap-mcu", GPIOD_OUT_HIGH);454	if (IS_ERR(data->ap_mcu_gpiod))455		return NULL;456 457	data->mcu_reset_gpiod = devm_gpiod_get(dev, "mcu-reset",458					       GPIOD_OUT_HIGH);459	if (IS_ERR(data->mcu_reset_gpiod))460		return NULL;461 462	data->sensorhub_info = device_get_match_data(dev);463 464	dev_set_drvdata(dev, data);465 466	return data;467}468 469/**470 * ssp_register_consumer() - registers iio consumer in ssp framework471 *472 * @indio_dev:	consumer iio device473 * @type:	ssp sensor type474 */475void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type type)476{477	struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);478 479	data->sensor_devs[type] = indio_dev;480}481EXPORT_SYMBOL_NS(ssp_register_consumer, IIO_SSP_SENSORS);482 483static int ssp_probe(struct spi_device *spi)484{485	int ret, i;486	struct ssp_data *data;487 488	data = ssp_parse_dt(&spi->dev);489	if (!data) {490		dev_err(&spi->dev, "Failed to find platform data\n");491		return -ENODEV;492	}493 494	ret = mfd_add_devices(&spi->dev, PLATFORM_DEVID_NONE,495			      sensorhub_sensor_devs,496			      ARRAY_SIZE(sensorhub_sensor_devs), NULL, 0, NULL);497	if (ret < 0) {498		dev_err(&spi->dev, "mfd add devices fail\n");499		return ret;500	}501 502	spi->mode = SPI_MODE_1;503	ret = spi_setup(spi);504	if (ret < 0) {505		dev_err(&spi->dev, "Failed to setup spi\n");506		return ret;507	}508 509	data->fw_dl_state = SSP_FW_DL_STATE_NONE;510	data->spi = spi;511	spi_set_drvdata(spi, data);512 513	mutex_init(&data->comm_lock);514 515	for (i = 0; i < SSP_SENSOR_MAX; ++i) {516		data->delay_buf[i] = SSP_DEFAULT_POLLING_DELAY;517		data->batch_latency_buf[i] = 0;518		data->batch_opt_buf[i] = 0;519		data->check_status[i] = SSP_INITIALIZATION_STATE;520	}521 522	data->delay_buf[SSP_BIO_HRM_LIB] = 100;523 524	data->time_syncing = true;525 526	mutex_init(&data->pending_lock);527	INIT_LIST_HEAD(&data->pending_list);528 529	atomic_set(&data->enable_refcount, 0);530 531	INIT_WORK(&data->work_wdt, ssp_wdt_work_func);532	INIT_DELAYED_WORK(&data->work_refresh, ssp_refresh_task);533 534	timer_setup(&data->wdt_timer, ssp_wdt_timer_func, 0);535 536	ret = request_threaded_irq(data->spi->irq, NULL,537				   ssp_irq_thread_fn,538				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,539				   "SSP_Int", data);540	if (ret < 0) {541		dev_err(&spi->dev, "Irq request fail\n");542		goto err_setup_irq;543	}544 545	/* Let's start with enabled one so irq balance could be ok */546	data->shut_down = false;547 548	/* just to avoid unbalanced irq set wake up */549	enable_irq_wake(data->spi->irq);550 551	data->fw_dl_state = ssp_check_fwbl(data);552	if (data->fw_dl_state == SSP_FW_DL_STATE_NONE) {553		ret = ssp_initialize_mcu(data);554		if (ret < 0) {555			dev_err(&spi->dev, "Initialize_mcu failed\n");556			goto err_read_reg;557		}558	} else {559		dev_err(&spi->dev, "Firmware version not supported\n");560		ret = -EPERM;561		goto err_read_reg;562	}563 564	return 0;565 566err_read_reg:567	free_irq(data->spi->irq, data);568err_setup_irq:569	mutex_destroy(&data->pending_lock);570	mutex_destroy(&data->comm_lock);571 572	dev_err(&spi->dev, "Probe failed!\n");573 574	return ret;575}576 577static void ssp_remove(struct spi_device *spi)578{579	struct ssp_data *data = spi_get_drvdata(spi);580 581	if (ssp_command(data, SSP_MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)582		dev_err(&data->spi->dev,583			"SSP_MSG2SSP_AP_STATUS_SHUTDOWN failed\n");584 585	ssp_enable_mcu(data, false);586	ssp_disable_wdt_timer(data);587 588	ssp_clean_pending_list(data);589 590	free_irq(data->spi->irq, data);591 592	del_timer_sync(&data->wdt_timer);593	cancel_work_sync(&data->work_wdt);594 595	mutex_destroy(&data->comm_lock);596	mutex_destroy(&data->pending_lock);597 598	mfd_remove_devices(&spi->dev);599}600 601static int ssp_suspend(struct device *dev)602{603	int ret;604	struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));605 606	data->last_resume_state = SSP_MSG2SSP_AP_STATUS_SUSPEND;607 608	if (atomic_read(&data->enable_refcount) > 0)609		ssp_disable_wdt_timer(data);610 611	ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_SUSPEND, 0);612	if (ret < 0) {613		dev_err(&data->spi->dev,614			"%s SSP_MSG2SSP_AP_STATUS_SUSPEND failed\n", __func__);615 616		ssp_enable_wdt_timer(data);617		return ret;618	}619 620	data->time_syncing = false;621	disable_irq(data->spi->irq);622 623	return 0;624}625 626static int ssp_resume(struct device *dev)627{628	int ret;629	struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));630 631	enable_irq(data->spi->irq);632 633	if (atomic_read(&data->enable_refcount) > 0)634		ssp_enable_wdt_timer(data);635 636	ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_RESUME, 0);637	if (ret < 0) {638		dev_err(&data->spi->dev,639			"%s SSP_MSG2SSP_AP_STATUS_RESUME failed\n", __func__);640		ssp_disable_wdt_timer(data);641		return ret;642	}643 644	/* timesyncing is set by MCU */645	data->last_resume_state = SSP_MSG2SSP_AP_STATUS_RESUME;646 647	return 0;648}649 650static DEFINE_SIMPLE_DEV_PM_OPS(ssp_pm_ops, ssp_suspend, ssp_resume);651 652static struct spi_driver ssp_driver = {653	.probe = ssp_probe,654	.remove = ssp_remove,655	.driver = {656		.pm = pm_sleep_ptr(&ssp_pm_ops),657		.of_match_table = ssp_of_match,658		.name = "sensorhub"659	},660};661 662module_spi_driver(ssp_driver);663 664MODULE_DESCRIPTION("ssp sensorhub driver");665MODULE_AUTHOR("Samsung Electronics");666MODULE_LICENSE("GPL");667