xref: /linux/drivers/cpuidle/cpuidle-qcom-spm.c (revision a871be6b8eee13a35a3e8e56c62770ef17ee9220)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27ce75bb2SLina Iyer /*
37ce75bb2SLina Iyer  * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
47ce75bb2SLina Iyer  * Copyright (c) 2014,2015, Linaro Ltd.
57ce75bb2SLina Iyer  *
63b904b04SLina Iyer  * SAW power controller driver
77ce75bb2SLina Iyer  */
87ce75bb2SLina Iyer 
97ce75bb2SLina Iyer #include <linux/kernel.h>
107ce75bb2SLina Iyer #include <linux/init.h>
117ce75bb2SLina Iyer #include <linux/io.h>
127ce75bb2SLina Iyer #include <linux/slab.h>
137ce75bb2SLina Iyer #include <linux/of.h>
147ce75bb2SLina Iyer #include <linux/of_address.h>
157ce75bb2SLina Iyer #include <linux/of_device.h>
167ce75bb2SLina Iyer #include <linux/err.h>
177ce75bb2SLina Iyer #include <linux/platform_device.h>
187ce75bb2SLina Iyer #include <linux/cpuidle.h>
197ce75bb2SLina Iyer #include <linux/cpu_pm.h>
207ce75bb2SLina Iyer #include <linux/qcom_scm.h>
217ce75bb2SLina Iyer 
227ce75bb2SLina Iyer #include <asm/proc-fns.h>
237ce75bb2SLina Iyer #include <asm/suspend.h>
247ce75bb2SLina Iyer 
25*a871be6bSStephan Gerhold #include "dt_idle_states.h"
26*a871be6bSStephan Gerhold 
277ce75bb2SLina Iyer #define MAX_PMIC_DATA		2
287ce75bb2SLina Iyer #define MAX_SEQ_DATA		64
297ce75bb2SLina Iyer #define SPM_CTL_INDEX		0x7f
307ce75bb2SLina Iyer #define SPM_CTL_INDEX_SHIFT	4
317ce75bb2SLina Iyer #define SPM_CTL_EN		BIT(0)
327ce75bb2SLina Iyer 
337ce75bb2SLina Iyer enum pm_sleep_mode {
347ce75bb2SLina Iyer 	PM_SLEEP_MODE_STBY,
357ce75bb2SLina Iyer 	PM_SLEEP_MODE_RET,
367ce75bb2SLina Iyer 	PM_SLEEP_MODE_SPC,
377ce75bb2SLina Iyer 	PM_SLEEP_MODE_PC,
387ce75bb2SLina Iyer 	PM_SLEEP_MODE_NR,
397ce75bb2SLina Iyer };
407ce75bb2SLina Iyer 
417ce75bb2SLina Iyer enum spm_reg {
427ce75bb2SLina Iyer 	SPM_REG_CFG,
437ce75bb2SLina Iyer 	SPM_REG_SPM_CTL,
447ce75bb2SLina Iyer 	SPM_REG_DLY,
457ce75bb2SLina Iyer 	SPM_REG_PMIC_DLY,
467ce75bb2SLina Iyer 	SPM_REG_PMIC_DATA_0,
477ce75bb2SLina Iyer 	SPM_REG_PMIC_DATA_1,
487ce75bb2SLina Iyer 	SPM_REG_VCTL,
497ce75bb2SLina Iyer 	SPM_REG_SEQ_ENTRY,
507ce75bb2SLina Iyer 	SPM_REG_SPM_STS,
517ce75bb2SLina Iyer 	SPM_REG_PMIC_STS,
527ce75bb2SLina Iyer 	SPM_REG_NR,
537ce75bb2SLina Iyer };
547ce75bb2SLina Iyer 
557ce75bb2SLina Iyer struct spm_reg_data {
567ce75bb2SLina Iyer 	const u8 *reg_offset;
577ce75bb2SLina Iyer 	u32 spm_cfg;
587ce75bb2SLina Iyer 	u32 spm_dly;
597ce75bb2SLina Iyer 	u32 pmic_dly;
607ce75bb2SLina Iyer 	u32 pmic_data[MAX_PMIC_DATA];
617ce75bb2SLina Iyer 	u8 seq[MAX_SEQ_DATA];
627ce75bb2SLina Iyer 	u8 start_index[PM_SLEEP_MODE_NR];
637ce75bb2SLina Iyer };
647ce75bb2SLina Iyer 
657ce75bb2SLina Iyer struct spm_driver_data {
66*a871be6bSStephan Gerhold 	struct cpuidle_driver cpuidle_driver;
677ce75bb2SLina Iyer 	void __iomem *reg_base;
687ce75bb2SLina Iyer 	const struct spm_reg_data *reg_data;
697ce75bb2SLina Iyer };
707ce75bb2SLina Iyer 
717ce75bb2SLina Iyer static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = {
727ce75bb2SLina Iyer 	[SPM_REG_CFG]		= 0x08,
737ce75bb2SLina Iyer 	[SPM_REG_SPM_CTL]	= 0x30,
747ce75bb2SLina Iyer 	[SPM_REG_DLY]		= 0x34,
757ce75bb2SLina Iyer 	[SPM_REG_SEQ_ENTRY]	= 0x80,
767ce75bb2SLina Iyer };
777ce75bb2SLina Iyer 
787ce75bb2SLina Iyer /* SPM register data for 8974, 8084 */
797ce75bb2SLina Iyer static const struct spm_reg_data spm_reg_8974_8084_cpu  = {
807ce75bb2SLina Iyer 	.reg_offset = spm_reg_offset_v2_1,
817ce75bb2SLina Iyer 	.spm_cfg = 0x1,
827ce75bb2SLina Iyer 	.spm_dly = 0x3C102800,
837ce75bb2SLina Iyer 	.seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
847ce75bb2SLina Iyer 		0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
857ce75bb2SLina Iyer 		0x0F },
867ce75bb2SLina Iyer 	.start_index[PM_SLEEP_MODE_STBY] = 0,
877ce75bb2SLina Iyer 	.start_index[PM_SLEEP_MODE_SPC] = 3,
887ce75bb2SLina Iyer };
897ce75bb2SLina Iyer 
907ce75bb2SLina Iyer static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = {
917ce75bb2SLina Iyer 	[SPM_REG_CFG]		= 0x08,
927ce75bb2SLina Iyer 	[SPM_REG_SPM_CTL]	= 0x20,
937ce75bb2SLina Iyer 	[SPM_REG_PMIC_DLY]	= 0x24,
947ce75bb2SLina Iyer 	[SPM_REG_PMIC_DATA_0]	= 0x28,
957ce75bb2SLina Iyer 	[SPM_REG_PMIC_DATA_1]	= 0x2C,
967ce75bb2SLina Iyer 	[SPM_REG_SEQ_ENTRY]	= 0x80,
977ce75bb2SLina Iyer };
987ce75bb2SLina Iyer 
997ce75bb2SLina Iyer /* SPM register data for 8064 */
1007ce75bb2SLina Iyer static const struct spm_reg_data spm_reg_8064_cpu = {
1017ce75bb2SLina Iyer 	.reg_offset = spm_reg_offset_v1_1,
1027ce75bb2SLina Iyer 	.spm_cfg = 0x1F,
1037ce75bb2SLina Iyer 	.pmic_dly = 0x02020004,
1047ce75bb2SLina Iyer 	.pmic_data[0] = 0x0084009C,
1057ce75bb2SLina Iyer 	.pmic_data[1] = 0x00A4001C,
1067ce75bb2SLina Iyer 	.seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
1077ce75bb2SLina Iyer 		0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
1087ce75bb2SLina Iyer 	.start_index[PM_SLEEP_MODE_STBY] = 0,
1097ce75bb2SLina Iyer 	.start_index[PM_SLEEP_MODE_SPC] = 2,
1107ce75bb2SLina Iyer };
1117ce75bb2SLina Iyer 
1127ce75bb2SLina Iyer static inline void spm_register_write(struct spm_driver_data *drv,
1137ce75bb2SLina Iyer 					enum spm_reg reg, u32 val)
1147ce75bb2SLina Iyer {
1157ce75bb2SLina Iyer 	if (drv->reg_data->reg_offset[reg])
1167ce75bb2SLina Iyer 		writel_relaxed(val, drv->reg_base +
1177ce75bb2SLina Iyer 				drv->reg_data->reg_offset[reg]);
1187ce75bb2SLina Iyer }
1197ce75bb2SLina Iyer 
1207ce75bb2SLina Iyer /* Ensure a guaranteed write, before return */
1217ce75bb2SLina Iyer static inline void spm_register_write_sync(struct spm_driver_data *drv,
1227ce75bb2SLina Iyer 					enum spm_reg reg, u32 val)
1237ce75bb2SLina Iyer {
1247ce75bb2SLina Iyer 	u32 ret;
1257ce75bb2SLina Iyer 
1267ce75bb2SLina Iyer 	if (!drv->reg_data->reg_offset[reg])
1277ce75bb2SLina Iyer 		return;
1287ce75bb2SLina Iyer 
1297ce75bb2SLina Iyer 	do {
1307ce75bb2SLina Iyer 		writel_relaxed(val, drv->reg_base +
1317ce75bb2SLina Iyer 				drv->reg_data->reg_offset[reg]);
1327ce75bb2SLina Iyer 		ret = readl_relaxed(drv->reg_base +
1337ce75bb2SLina Iyer 				drv->reg_data->reg_offset[reg]);
1347ce75bb2SLina Iyer 		if (ret == val)
1357ce75bb2SLina Iyer 			break;
1367ce75bb2SLina Iyer 		cpu_relax();
1377ce75bb2SLina Iyer 	} while (1);
1387ce75bb2SLina Iyer }
1397ce75bb2SLina Iyer 
1407ce75bb2SLina Iyer static inline u32 spm_register_read(struct spm_driver_data *drv,
1417ce75bb2SLina Iyer 					enum spm_reg reg)
1427ce75bb2SLina Iyer {
1437ce75bb2SLina Iyer 	return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
1447ce75bb2SLina Iyer }
1457ce75bb2SLina Iyer 
1467ce75bb2SLina Iyer static void spm_set_low_power_mode(struct spm_driver_data *drv,
1477ce75bb2SLina Iyer 					enum pm_sleep_mode mode)
1487ce75bb2SLina Iyer {
1497ce75bb2SLina Iyer 	u32 start_index;
1507ce75bb2SLina Iyer 	u32 ctl_val;
1517ce75bb2SLina Iyer 
1527ce75bb2SLina Iyer 	start_index = drv->reg_data->start_index[mode];
1537ce75bb2SLina Iyer 
1547ce75bb2SLina Iyer 	ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
1557ce75bb2SLina Iyer 	ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
1567ce75bb2SLina Iyer 	ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
1577ce75bb2SLina Iyer 	ctl_val |= SPM_CTL_EN;
1587ce75bb2SLina Iyer 	spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
1597ce75bb2SLina Iyer }
1607ce75bb2SLina Iyer 
1617ce75bb2SLina Iyer static int qcom_pm_collapse(unsigned long int unused)
1627ce75bb2SLina Iyer {
1637ce75bb2SLina Iyer 	qcom_scm_cpu_power_down(QCOM_SCM_CPU_PWR_DOWN_L2_ON);
1647ce75bb2SLina Iyer 
1657ce75bb2SLina Iyer 	/*
1667ce75bb2SLina Iyer 	 * Returns here only if there was a pending interrupt and we did not
1677ce75bb2SLina Iyer 	 * power down as a result.
1687ce75bb2SLina Iyer 	 */
1697ce75bb2SLina Iyer 	return -1;
1707ce75bb2SLina Iyer }
1717ce75bb2SLina Iyer 
172*a871be6bSStephan Gerhold static int qcom_cpu_spc(struct spm_driver_data *drv)
1737ce75bb2SLina Iyer {
1747ce75bb2SLina Iyer 	int ret;
1757ce75bb2SLina Iyer 
1767ce75bb2SLina Iyer 	spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC);
1777ce75bb2SLina Iyer 	ret = cpu_suspend(0, qcom_pm_collapse);
1787ce75bb2SLina Iyer 	/*
1797ce75bb2SLina Iyer 	 * ARM common code executes WFI without calling into our driver and
1807ce75bb2SLina Iyer 	 * if the SPM mode is not reset, then we may accidently power down the
1817ce75bb2SLina Iyer 	 * cpu when we intended only to gate the cpu clock.
1827ce75bb2SLina Iyer 	 * Ensure the state is set to standby before returning.
1837ce75bb2SLina Iyer 	 */
1847ce75bb2SLina Iyer 	spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
1857ce75bb2SLina Iyer 
1867ce75bb2SLina Iyer 	return ret;
1877ce75bb2SLina Iyer }
1887ce75bb2SLina Iyer 
189*a871be6bSStephan Gerhold static int spm_enter_idle_state(struct cpuidle_device *dev,
190*a871be6bSStephan Gerhold 				struct cpuidle_driver *drv, int idx)
1917ce75bb2SLina Iyer {
192*a871be6bSStephan Gerhold 	struct spm_driver_data *data = container_of(drv, struct spm_driver_data,
193*a871be6bSStephan Gerhold 						    cpuidle_driver);
194*a871be6bSStephan Gerhold 
195*a871be6bSStephan Gerhold 	return CPU_PM_CPU_IDLE_ENTER_PARAM(qcom_cpu_spc, idx, data);
1967ce75bb2SLina Iyer }
1977ce75bb2SLina Iyer 
198*a871be6bSStephan Gerhold static struct cpuidle_driver qcom_spm_idle_driver = {
199*a871be6bSStephan Gerhold 	.name = "qcom_spm",
200*a871be6bSStephan Gerhold 	.owner = THIS_MODULE,
201*a871be6bSStephan Gerhold 	.states[0] = {
202*a871be6bSStephan Gerhold 		.enter			= spm_enter_idle_state,
203*a871be6bSStephan Gerhold 		.exit_latency		= 1,
204*a871be6bSStephan Gerhold 		.target_residency	= 1,
205*a871be6bSStephan Gerhold 		.power_usage		= UINT_MAX,
206*a871be6bSStephan Gerhold 		.name			= "WFI",
207*a871be6bSStephan Gerhold 		.desc			= "ARM WFI",
208*a871be6bSStephan Gerhold 	}
209*a871be6bSStephan Gerhold };
210*a871be6bSStephan Gerhold 
211*a871be6bSStephan Gerhold static const struct of_device_id qcom_idle_state_match[] = {
212*a871be6bSStephan Gerhold 	{ .compatible = "qcom,idle-state-spc", .data = spm_enter_idle_state },
2137ce75bb2SLina Iyer 	{ },
2147ce75bb2SLina Iyer };
2157ce75bb2SLina Iyer 
216*a871be6bSStephan Gerhold static int spm_cpuidle_init(struct cpuidle_driver *drv, int cpu)
2177ce75bb2SLina Iyer {
218*a871be6bSStephan Gerhold 	int ret;
2197ce75bb2SLina Iyer 
220*a871be6bSStephan Gerhold 	memcpy(drv, &qcom_spm_idle_driver, sizeof(*drv));
221*a871be6bSStephan Gerhold 	drv->cpumask = (struct cpumask *)cpumask_of(cpu);
22261a3bd10SFelix Fietkau 
223*a871be6bSStephan Gerhold 	/* Parse idle states from device tree */
224*a871be6bSStephan Gerhold 	ret = dt_init_idle_driver(drv, qcom_idle_state_match, 1);
225*a871be6bSStephan Gerhold 	if (ret <= 0)
226*a871be6bSStephan Gerhold 		return ret ? : -ENODEV;
2277ce75bb2SLina Iyer 
2287ce75bb2SLina Iyer 	/* We have atleast one power down mode */
229*a871be6bSStephan Gerhold 	return qcom_scm_set_warm_boot_addr(cpu_resume_arm, drv->cpumask);
2307ce75bb2SLina Iyer }
2317ce75bb2SLina Iyer 
2327ce75bb2SLina Iyer static struct spm_driver_data *spm_get_drv(struct platform_device *pdev,
2337ce75bb2SLina Iyer 		int *spm_cpu)
2347ce75bb2SLina Iyer {
2357ce75bb2SLina Iyer 	struct spm_driver_data *drv = NULL;
2367ce75bb2SLina Iyer 	struct device_node *cpu_node, *saw_node;
2377ce75bb2SLina Iyer 	int cpu;
23800affcacSArnd Bergmann 	bool found = 0;
2397ce75bb2SLina Iyer 
2407ce75bb2SLina Iyer 	for_each_possible_cpu(cpu) {
2417ce75bb2SLina Iyer 		cpu_node = of_cpu_device_node_get(cpu);
2427ce75bb2SLina Iyer 		if (!cpu_node)
2437ce75bb2SLina Iyer 			continue;
2447ce75bb2SLina Iyer 		saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
2457ce75bb2SLina Iyer 		found = (saw_node == pdev->dev.of_node);
2467ce75bb2SLina Iyer 		of_node_put(saw_node);
2477ce75bb2SLina Iyer 		of_node_put(cpu_node);
2487ce75bb2SLina Iyer 		if (found)
2497ce75bb2SLina Iyer 			break;
2507ce75bb2SLina Iyer 	}
2517ce75bb2SLina Iyer 
2527ce75bb2SLina Iyer 	if (found) {
2537ce75bb2SLina Iyer 		drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
2547ce75bb2SLina Iyer 		if (drv)
2557ce75bb2SLina Iyer 			*spm_cpu = cpu;
2567ce75bb2SLina Iyer 	}
2577ce75bb2SLina Iyer 
2587ce75bb2SLina Iyer 	return drv;
2597ce75bb2SLina Iyer }
2607ce75bb2SLina Iyer 
2617ce75bb2SLina Iyer static const struct of_device_id spm_match_table[] = {
2627ce75bb2SLina Iyer 	{ .compatible = "qcom,msm8974-saw2-v2.1-cpu",
2637ce75bb2SLina Iyer 	  .data = &spm_reg_8974_8084_cpu },
2647ce75bb2SLina Iyer 	{ .compatible = "qcom,apq8084-saw2-v2.1-cpu",
2657ce75bb2SLina Iyer 	  .data = &spm_reg_8974_8084_cpu },
2667ce75bb2SLina Iyer 	{ .compatible = "qcom,apq8064-saw2-v1.1-cpu",
2677ce75bb2SLina Iyer 	  .data = &spm_reg_8064_cpu },
2687ce75bb2SLina Iyer 	{ },
2697ce75bb2SLina Iyer };
2707ce75bb2SLina Iyer 
2717ce75bb2SLina Iyer static int spm_dev_probe(struct platform_device *pdev)
2727ce75bb2SLina Iyer {
2737ce75bb2SLina Iyer 	struct spm_driver_data *drv;
2747ce75bb2SLina Iyer 	struct resource *res;
2757ce75bb2SLina Iyer 	const struct of_device_id *match_id;
2767ce75bb2SLina Iyer 	void __iomem *addr;
277*a871be6bSStephan Gerhold 	int cpu, ret;
278*a871be6bSStephan Gerhold 
279*a871be6bSStephan Gerhold 	if (!qcom_scm_is_available())
280*a871be6bSStephan Gerhold 		return -EPROBE_DEFER;
2817ce75bb2SLina Iyer 
2827ce75bb2SLina Iyer 	drv = spm_get_drv(pdev, &cpu);
2837ce75bb2SLina Iyer 	if (!drv)
2847ce75bb2SLina Iyer 		return -EINVAL;
285*a871be6bSStephan Gerhold 	platform_set_drvdata(pdev, drv);
2867ce75bb2SLina Iyer 
2877ce75bb2SLina Iyer 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2887ce75bb2SLina Iyer 	drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
2897ce75bb2SLina Iyer 	if (IS_ERR(drv->reg_base))
2907ce75bb2SLina Iyer 		return PTR_ERR(drv->reg_base);
2917ce75bb2SLina Iyer 
2927ce75bb2SLina Iyer 	match_id = of_match_node(spm_match_table, pdev->dev.of_node);
2937ce75bb2SLina Iyer 	if (!match_id)
2947ce75bb2SLina Iyer 		return -ENODEV;
2957ce75bb2SLina Iyer 
2967ce75bb2SLina Iyer 	drv->reg_data = match_id->data;
2977ce75bb2SLina Iyer 
298*a871be6bSStephan Gerhold 	ret = spm_cpuidle_init(&drv->cpuidle_driver, cpu);
299*a871be6bSStephan Gerhold 	if (ret)
300*a871be6bSStephan Gerhold 		return ret;
301*a871be6bSStephan Gerhold 
3027ce75bb2SLina Iyer 	/* Write the SPM sequences first.. */
3037ce75bb2SLina Iyer 	addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
3047ce75bb2SLina Iyer 	__iowrite32_copy(addr, drv->reg_data->seq,
3057ce75bb2SLina Iyer 			ARRAY_SIZE(drv->reg_data->seq) / 4);
3067ce75bb2SLina Iyer 
3077ce75bb2SLina Iyer 	/*
3087ce75bb2SLina Iyer 	 * ..and then the control registers.
3097ce75bb2SLina Iyer 	 * On some SoC if the control registers are written first and if the
3107ce75bb2SLina Iyer 	 * CPU was held in reset, the reset signal could trigger the SPM state
3117ce75bb2SLina Iyer 	 * machine, before the sequences are completely written.
3127ce75bb2SLina Iyer 	 */
3137ce75bb2SLina Iyer 	spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
3147ce75bb2SLina Iyer 	spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
3157ce75bb2SLina Iyer 	spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
3167ce75bb2SLina Iyer 	spm_register_write(drv, SPM_REG_PMIC_DATA_0,
3177ce75bb2SLina Iyer 				drv->reg_data->pmic_data[0]);
3187ce75bb2SLina Iyer 	spm_register_write(drv, SPM_REG_PMIC_DATA_1,
3197ce75bb2SLina Iyer 				drv->reg_data->pmic_data[1]);
3207ce75bb2SLina Iyer 
3217ce75bb2SLina Iyer 	/* Set up Standby as the default low power mode */
3227ce75bb2SLina Iyer 	spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY);
3237ce75bb2SLina Iyer 
324*a871be6bSStephan Gerhold 	return cpuidle_register(&drv->cpuidle_driver, NULL);
325*a871be6bSStephan Gerhold }
3267ce75bb2SLina Iyer 
327*a871be6bSStephan Gerhold static int spm_dev_remove(struct platform_device *pdev)
328*a871be6bSStephan Gerhold {
329*a871be6bSStephan Gerhold 	struct spm_driver_data *drv = platform_get_drvdata(pdev);
330*a871be6bSStephan Gerhold 
331*a871be6bSStephan Gerhold 	cpuidle_unregister(&drv->cpuidle_driver);
3327ce75bb2SLina Iyer 	return 0;
3337ce75bb2SLina Iyer }
3347ce75bb2SLina Iyer 
3357ce75bb2SLina Iyer static struct platform_driver spm_driver = {
3367ce75bb2SLina Iyer 	.probe = spm_dev_probe,
337*a871be6bSStephan Gerhold 	.remove = spm_dev_remove,
3387ce75bb2SLina Iyer 	.driver = {
3397ce75bb2SLina Iyer 		.name = "saw",
3407ce75bb2SLina Iyer 		.of_match_table = spm_match_table,
3417ce75bb2SLina Iyer 	},
3427ce75bb2SLina Iyer };
3437ce75bb2SLina Iyer 
3443b904b04SLina Iyer builtin_platform_driver(spm_driver);
345