xref: /linux/drivers/net/can/sja1000/sja1000_platform.c (revision 9410645520e9b820069761f3450ef6661418e279)
135e62ae8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f534e52fSWolfgang Grandegger /*
3f534e52fSWolfgang Grandegger  * Copyright (C) 2005 Sascha Hauer, Pengutronix
4f534e52fSWolfgang Grandegger  * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
5f534e52fSWolfgang Grandegger  */
6f534e52fSWolfgang Grandegger 
7f534e52fSWolfgang Grandegger #include <linux/kernel.h>
8f534e52fSWolfgang Grandegger #include <linux/module.h>
9f534e52fSWolfgang Grandegger #include <linux/interrupt.h>
10f534e52fSWolfgang Grandegger #include <linux/netdevice.h>
11f534e52fSWolfgang Grandegger #include <linux/delay.h>
12f534e52fSWolfgang Grandegger #include <linux/pci.h>
13f534e52fSWolfgang Grandegger #include <linux/platform_device.h>
14f534e52fSWolfgang Grandegger #include <linux/irq.h>
15f534e52fSWolfgang Grandegger #include <linux/can/dev.h>
16f534e52fSWolfgang Grandegger #include <linux/can/platform/sja1000.h>
170838921bSBiju Das #include <linux/clk.h>
18f534e52fSWolfgang Grandegger #include <linux/io.h>
1902729c3dSFlorian Vaussard #include <linux/of.h>
20f534e52fSWolfgang Grandegger 
21f534e52fSWolfgang Grandegger #include "sja1000.h"
22f534e52fSWolfgang Grandegger 
23f534e52fSWolfgang Grandegger #define DRV_NAME "sja1000_platform"
2402729c3dSFlorian Vaussard #define SP_CAN_CLOCK  (16000000 / 2)
25f534e52fSWolfgang Grandegger 
26f534e52fSWolfgang Grandegger MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
2702729c3dSFlorian Vaussard MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
28f534e52fSWolfgang Grandegger MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus");
29d8c4386dSMarc Kleine-Budde MODULE_ALIAS("platform:" DRV_NAME);
30f534e52fSWolfgang Grandegger MODULE_LICENSE("GPL v2");
31f534e52fSWolfgang Grandegger 
32f49cbe6bSDamien Riegel struct sja1000_of_data {
33f49cbe6bSDamien Riegel 	size_t  priv_sz;
346d5fe107SBiju Das 	void    (*init)(struct sja1000_priv *priv, struct device_node *of);
35f49cbe6bSDamien Riegel };
36f49cbe6bSDamien Riegel 
37dfb86c0dSDamien Riegel struct technologic_priv {
38dfb86c0dSDamien Riegel 	spinlock_t      io_lock;
39dfb86c0dSDamien Riegel };
40dfb86c0dSDamien Riegel 
sp_read_reg8(const struct sja1000_priv * priv,int reg)41986917b7SYegor Yefremov static u8 sp_read_reg8(const struct sja1000_priv *priv, int reg)
42f534e52fSWolfgang Grandegger {
43255a9154SWolfgang Grandegger 	return ioread8(priv->reg_base + reg);
44f534e52fSWolfgang Grandegger }
45f534e52fSWolfgang Grandegger 
sp_write_reg8(const struct sja1000_priv * priv,int reg,u8 val)46986917b7SYegor Yefremov static void sp_write_reg8(const struct sja1000_priv *priv, int reg, u8 val)
47f534e52fSWolfgang Grandegger {
48255a9154SWolfgang Grandegger 	iowrite8(val, priv->reg_base + reg);
49f534e52fSWolfgang Grandegger }
50f534e52fSWolfgang Grandegger 
sp_read_reg16(const struct sja1000_priv * priv,int reg)51986917b7SYegor Yefremov static u8 sp_read_reg16(const struct sja1000_priv *priv, int reg)
52986917b7SYegor Yefremov {
53986917b7SYegor Yefremov 	return ioread8(priv->reg_base + reg * 2);
54986917b7SYegor Yefremov }
55986917b7SYegor Yefremov 
sp_write_reg16(const struct sja1000_priv * priv,int reg,u8 val)56986917b7SYegor Yefremov static void sp_write_reg16(const struct sja1000_priv *priv, int reg, u8 val)
57986917b7SYegor Yefremov {
58986917b7SYegor Yefremov 	iowrite8(val, priv->reg_base + reg * 2);
59986917b7SYegor Yefremov }
60986917b7SYegor Yefremov 
sp_read_reg32(const struct sja1000_priv * priv,int reg)61986917b7SYegor Yefremov static u8 sp_read_reg32(const struct sja1000_priv *priv, int reg)
62986917b7SYegor Yefremov {
63986917b7SYegor Yefremov 	return ioread8(priv->reg_base + reg * 4);
64986917b7SYegor Yefremov }
65986917b7SYegor Yefremov 
sp_write_reg32(const struct sja1000_priv * priv,int reg,u8 val)66986917b7SYegor Yefremov static void sp_write_reg32(const struct sja1000_priv *priv, int reg, u8 val)
67986917b7SYegor Yefremov {
68986917b7SYegor Yefremov 	iowrite8(val, priv->reg_base + reg * 4);
69986917b7SYegor Yefremov }
70986917b7SYegor Yefremov 
sp_technologic_read_reg16(const struct sja1000_priv * priv,int reg)71dfb86c0dSDamien Riegel static u8 sp_technologic_read_reg16(const struct sja1000_priv *priv, int reg)
72dfb86c0dSDamien Riegel {
73dfb86c0dSDamien Riegel 	struct technologic_priv *tp = priv->priv;
74dfb86c0dSDamien Riegel 	unsigned long flags;
75dfb86c0dSDamien Riegel 	u8 val;
76dfb86c0dSDamien Riegel 
77dfb86c0dSDamien Riegel 	spin_lock_irqsave(&tp->io_lock, flags);
78dfb86c0dSDamien Riegel 	iowrite16(reg, priv->reg_base + 0);
79dfb86c0dSDamien Riegel 	val = ioread16(priv->reg_base + 2);
80dfb86c0dSDamien Riegel 	spin_unlock_irqrestore(&tp->io_lock, flags);
81dfb86c0dSDamien Riegel 
82dfb86c0dSDamien Riegel 	return val;
83dfb86c0dSDamien Riegel }
84dfb86c0dSDamien Riegel 
sp_technologic_write_reg16(const struct sja1000_priv * priv,int reg,u8 val)85dfb86c0dSDamien Riegel static void sp_technologic_write_reg16(const struct sja1000_priv *priv,
86dfb86c0dSDamien Riegel 				       int reg, u8 val)
87dfb86c0dSDamien Riegel {
88dfb86c0dSDamien Riegel 	struct technologic_priv *tp = priv->priv;
89dfb86c0dSDamien Riegel 	unsigned long flags;
90dfb86c0dSDamien Riegel 
91dfb86c0dSDamien Riegel 	spin_lock_irqsave(&tp->io_lock, flags);
92dfb86c0dSDamien Riegel 	iowrite16(reg, priv->reg_base + 0);
93dfb86c0dSDamien Riegel 	iowrite16(val, priv->reg_base + 2);
94dfb86c0dSDamien Riegel 	spin_unlock_irqrestore(&tp->io_lock, flags);
95dfb86c0dSDamien Riegel }
96dfb86c0dSDamien Riegel 
sp_technologic_init(struct sja1000_priv * priv,struct device_node * of)976d5fe107SBiju Das static void sp_technologic_init(struct sja1000_priv *priv, struct device_node *of)
98dfb86c0dSDamien Riegel {
99dfb86c0dSDamien Riegel 	struct technologic_priv *tp = priv->priv;
100dfb86c0dSDamien Riegel 
101dfb86c0dSDamien Riegel 	priv->read_reg = sp_technologic_read_reg16;
102dfb86c0dSDamien Riegel 	priv->write_reg = sp_technologic_write_reg16;
103dfb86c0dSDamien Riegel 	spin_lock_init(&tp->io_lock);
104dfb86c0dSDamien Riegel }
105dfb86c0dSDamien Riegel 
sp_rzn1_init(struct sja1000_priv * priv,struct device_node * of)1060838921bSBiju Das static void sp_rzn1_init(struct sja1000_priv *priv, struct device_node *of)
1070838921bSBiju Das {
108717c6ec2SMiquel Raynal 	priv->flags = SJA1000_QUIRK_NO_CDR_REG | SJA1000_QUIRK_RESET_ON_OVERRUN;
1090838921bSBiju Das }
1100838921bSBiju Das 
sp_populate(struct sja1000_priv * priv,struct sja1000_platform_data * pdata,unsigned long resource_mem_flags)11102729c3dSFlorian Vaussard static void sp_populate(struct sja1000_priv *priv,
11202729c3dSFlorian Vaussard 			struct sja1000_platform_data *pdata,
11302729c3dSFlorian Vaussard 			unsigned long resource_mem_flags)
114f534e52fSWolfgang Grandegger {
11556e6943bSWolfgang Grandegger 	/* The CAN clock frequency is half the oscillator clock frequency */
11656e6943bSWolfgang Grandegger 	priv->can.clock.freq = pdata->osc_freq / 2;
117f534e52fSWolfgang Grandegger 	priv->ocr = pdata->ocr;
118f534e52fSWolfgang Grandegger 	priv->cdr = pdata->cdr;
119f534e52fSWolfgang Grandegger 
12002729c3dSFlorian Vaussard 	switch (resource_mem_flags & IORESOURCE_MEM_TYPE_MASK) {
121986917b7SYegor Yefremov 	case IORESOURCE_MEM_32BIT:
122986917b7SYegor Yefremov 		priv->read_reg = sp_read_reg32;
123986917b7SYegor Yefremov 		priv->write_reg = sp_write_reg32;
124986917b7SYegor Yefremov 		break;
125986917b7SYegor Yefremov 	case IORESOURCE_MEM_16BIT:
126986917b7SYegor Yefremov 		priv->read_reg = sp_read_reg16;
127986917b7SYegor Yefremov 		priv->write_reg = sp_write_reg16;
128986917b7SYegor Yefremov 		break;
129986917b7SYegor Yefremov 	case IORESOURCE_MEM_8BIT:
130986917b7SYegor Yefremov 	default:
131986917b7SYegor Yefremov 		priv->read_reg = sp_read_reg8;
132986917b7SYegor Yefremov 		priv->write_reg = sp_write_reg8;
133986917b7SYegor Yefremov 		break;
134986917b7SYegor Yefremov 	}
13502729c3dSFlorian Vaussard }
13602729c3dSFlorian Vaussard 
sp_populate_of(struct sja1000_priv * priv,struct device_node * of)13702729c3dSFlorian Vaussard static void sp_populate_of(struct sja1000_priv *priv, struct device_node *of)
13802729c3dSFlorian Vaussard {
13902729c3dSFlorian Vaussard 	int err;
14002729c3dSFlorian Vaussard 	u32 prop;
14102729c3dSFlorian Vaussard 
142b18ec27cSFlorian Vaussard 	err = of_property_read_u32(of, "reg-io-width", &prop);
143b18ec27cSFlorian Vaussard 	if (err)
144b18ec27cSFlorian Vaussard 		prop = 1; /* 8 bit is default */
145b18ec27cSFlorian Vaussard 
146b18ec27cSFlorian Vaussard 	switch (prop) {
147b18ec27cSFlorian Vaussard 	case 4:
148b18ec27cSFlorian Vaussard 		priv->read_reg = sp_read_reg32;
149b18ec27cSFlorian Vaussard 		priv->write_reg = sp_write_reg32;
150b18ec27cSFlorian Vaussard 		break;
151b18ec27cSFlorian Vaussard 	case 2:
152b18ec27cSFlorian Vaussard 		priv->read_reg = sp_read_reg16;
153b18ec27cSFlorian Vaussard 		priv->write_reg = sp_write_reg16;
154b18ec27cSFlorian Vaussard 		break;
155df561f66SGustavo A. R. Silva 	case 1:
156b18ec27cSFlorian Vaussard 	default:
15702729c3dSFlorian Vaussard 		priv->read_reg = sp_read_reg8;
15802729c3dSFlorian Vaussard 		priv->write_reg = sp_write_reg8;
159b18ec27cSFlorian Vaussard 	}
16002729c3dSFlorian Vaussard 
1610838921bSBiju Das 	if (!priv->can.clock.freq) {
16202729c3dSFlorian Vaussard 		err = of_property_read_u32(of, "nxp,external-clock-frequency", &prop);
16302729c3dSFlorian Vaussard 		if (!err)
16402729c3dSFlorian Vaussard 			priv->can.clock.freq = prop / 2;
16502729c3dSFlorian Vaussard 		else
16602729c3dSFlorian Vaussard 			priv->can.clock.freq = SP_CAN_CLOCK; /* default */
1670838921bSBiju Das 	}
16802729c3dSFlorian Vaussard 
16902729c3dSFlorian Vaussard 	err = of_property_read_u32(of, "nxp,tx-output-mode", &prop);
17002729c3dSFlorian Vaussard 	if (!err)
17102729c3dSFlorian Vaussard 		priv->ocr |= prop & OCR_MODE_MASK;
17202729c3dSFlorian Vaussard 	else
17302729c3dSFlorian Vaussard 		priv->ocr |= OCR_MODE_NORMAL; /* default */
17402729c3dSFlorian Vaussard 
17502729c3dSFlorian Vaussard 	err = of_property_read_u32(of, "nxp,tx-output-config", &prop);
17602729c3dSFlorian Vaussard 	if (!err)
17702729c3dSFlorian Vaussard 		priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK;
17802729c3dSFlorian Vaussard 	else
17902729c3dSFlorian Vaussard 		priv->ocr |= OCR_TX0_PULLDOWN; /* default */
18002729c3dSFlorian Vaussard 
18102729c3dSFlorian Vaussard 	err = of_property_read_u32(of, "nxp,clock-out-frequency", &prop);
18202729c3dSFlorian Vaussard 	if (!err && prop) {
18302729c3dSFlorian Vaussard 		u32 divider = priv->can.clock.freq * 2 / prop;
18402729c3dSFlorian Vaussard 
18502729c3dSFlorian Vaussard 		if (divider > 1)
18602729c3dSFlorian Vaussard 			priv->cdr |= divider / 2 - 1;
18702729c3dSFlorian Vaussard 		else
18802729c3dSFlorian Vaussard 			priv->cdr |= CDR_CLKOUT_MASK;
18902729c3dSFlorian Vaussard 	} else {
19002729c3dSFlorian Vaussard 		priv->cdr |= CDR_CLK_OFF; /* default */
19102729c3dSFlorian Vaussard 	}
19202729c3dSFlorian Vaussard 
19302729c3dSFlorian Vaussard 	if (!of_property_read_bool(of, "nxp,no-comparator-bypass"))
19402729c3dSFlorian Vaussard 		priv->cdr |= CDR_CBP; /* default */
19502729c3dSFlorian Vaussard }
19602729c3dSFlorian Vaussard 
197dfb86c0dSDamien Riegel static struct sja1000_of_data technologic_data = {
198dfb86c0dSDamien Riegel 	.priv_sz = sizeof(struct technologic_priv),
199dfb86c0dSDamien Riegel 	.init = sp_technologic_init,
200dfb86c0dSDamien Riegel };
201dfb86c0dSDamien Riegel 
2020838921bSBiju Das static struct sja1000_of_data renesas_data = {
2030838921bSBiju Das 	.init = sp_rzn1_init,
2040838921bSBiju Das };
2050838921bSBiju Das 
206f49cbe6bSDamien Riegel static const struct of_device_id sp_of_table[] = {
207f49cbe6bSDamien Riegel 	{ .compatible = "nxp,sja1000", .data = NULL, },
2080838921bSBiju Das 	{ .compatible = "renesas,rzn1-sja1000", .data = &renesas_data, },
209dfb86c0dSDamien Riegel 	{ .compatible = "technologic,sja1000", .data = &technologic_data, },
210f49cbe6bSDamien Riegel 	{ /* sentinel */ },
211f49cbe6bSDamien Riegel };
212f49cbe6bSDamien Riegel MODULE_DEVICE_TABLE(of, sp_of_table);
213f49cbe6bSDamien Riegel 
sp_probe(struct platform_device * pdev)21402729c3dSFlorian Vaussard static int sp_probe(struct platform_device *pdev)
21502729c3dSFlorian Vaussard {
21602729c3dSFlorian Vaussard 	int err, irq = 0;
21702729c3dSFlorian Vaussard 	void __iomem *addr;
21802729c3dSFlorian Vaussard 	struct net_device *dev;
21902729c3dSFlorian Vaussard 	struct sja1000_priv *priv;
22002729c3dSFlorian Vaussard 	struct resource *res_mem, *res_irq = NULL;
22102729c3dSFlorian Vaussard 	struct sja1000_platform_data *pdata;
22202729c3dSFlorian Vaussard 	struct device_node *of = pdev->dev.of_node;
223f49cbe6bSDamien Riegel 	const struct sja1000_of_data *of_data = NULL;
224f49cbe6bSDamien Riegel 	size_t priv_sz = 0;
2250838921bSBiju Das 	struct clk *clk;
22602729c3dSFlorian Vaussard 
22702729c3dSFlorian Vaussard 	pdata = dev_get_platdata(&pdev->dev);
22802729c3dSFlorian Vaussard 	if (!pdata && !of) {
22902729c3dSFlorian Vaussard 		dev_err(&pdev->dev, "No platform data provided!\n");
23002729c3dSFlorian Vaussard 		return -ENODEV;
23102729c3dSFlorian Vaussard 	}
23202729c3dSFlorian Vaussard 
23302729c3dSFlorian Vaussard 	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
23402729c3dSFlorian Vaussard 	if (!res_mem)
23502729c3dSFlorian Vaussard 		return -ENODEV;
23602729c3dSFlorian Vaussard 
23702729c3dSFlorian Vaussard 	if (!devm_request_mem_region(&pdev->dev, res_mem->start,
23802729c3dSFlorian Vaussard 				     resource_size(res_mem), DRV_NAME))
23902729c3dSFlorian Vaussard 		return -EBUSY;
24002729c3dSFlorian Vaussard 
2414bdc0d67SChristoph Hellwig 	addr = devm_ioremap(&pdev->dev, res_mem->start,
24202729c3dSFlorian Vaussard 				    resource_size(res_mem));
24302729c3dSFlorian Vaussard 	if (!addr)
24402729c3dSFlorian Vaussard 		return -ENOMEM;
24502729c3dSFlorian Vaussard 
246decdcaeeSLad Prabhakar 	if (of) {
247decdcaeeSLad Prabhakar 		irq = platform_get_irq(pdev, 0);
248decdcaeeSLad Prabhakar 		if (irq < 0)
249decdcaeeSLad Prabhakar 			return irq;
2500838921bSBiju Das 
2510838921bSBiju Das 		clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
2520838921bSBiju Das 		if (IS_ERR(clk))
2530838921bSBiju Das 			return dev_err_probe(&pdev->dev, PTR_ERR(clk),
2540838921bSBiju Das 					     "CAN clk operation failed");
255decdcaeeSLad Prabhakar 	} else {
25602729c3dSFlorian Vaussard 		res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
257decdcaeeSLad Prabhakar 		if (!res_irq)
25802729c3dSFlorian Vaussard 			return -ENODEV;
259decdcaeeSLad Prabhakar 	}
26002729c3dSFlorian Vaussard 
26163ab1b63SBiju Das 	of_data = device_get_match_data(&pdev->dev);
26263ab1b63SBiju Das 	if (of_data)
263f49cbe6bSDamien Riegel 		priv_sz = of_data->priv_sz;
264f49cbe6bSDamien Riegel 
265f49cbe6bSDamien Riegel 	dev = alloc_sja1000dev(priv_sz);
26602729c3dSFlorian Vaussard 	if (!dev)
26702729c3dSFlorian Vaussard 		return -ENOMEM;
26802729c3dSFlorian Vaussard 	priv = netdev_priv(dev);
26902729c3dSFlorian Vaussard 
27002729c3dSFlorian Vaussard 	if (res_irq) {
27102729c3dSFlorian Vaussard 		irq = res_irq->start;
27202729c3dSFlorian Vaussard 		priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
27302729c3dSFlorian Vaussard 		if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
27402729c3dSFlorian Vaussard 			priv->irq_flags |= IRQF_SHARED;
27502729c3dSFlorian Vaussard 	} else {
27602729c3dSFlorian Vaussard 		priv->irq_flags = IRQF_SHARED;
27702729c3dSFlorian Vaussard 	}
27802729c3dSFlorian Vaussard 
279717c6ec2SMiquel Raynal 	if (priv->flags & SJA1000_QUIRK_RESET_ON_OVERRUN)
280717c6ec2SMiquel Raynal 		priv->irq_flags |= IRQF_ONESHOT;
281717c6ec2SMiquel Raynal 
28202729c3dSFlorian Vaussard 	dev->irq = irq;
28302729c3dSFlorian Vaussard 	priv->reg_base = addr;
28402729c3dSFlorian Vaussard 
285f49cbe6bSDamien Riegel 	if (of) {
2860838921bSBiju Das 		if (clk) {
2870838921bSBiju Das 			priv->can.clock.freq  = clk_get_rate(clk) / 2;
2880838921bSBiju Das 			if (!priv->can.clock.freq) {
2890838921bSBiju Das 				err = -EINVAL;
2900838921bSBiju Das 				dev_err(&pdev->dev, "Zero CAN clk rate");
2910838921bSBiju Das 				goto exit_free;
2920838921bSBiju Das 			}
2930838921bSBiju Das 		}
2940838921bSBiju Das 
29502729c3dSFlorian Vaussard 		sp_populate_of(priv, of);
296f49cbe6bSDamien Riegel 
2976d5fe107SBiju Das 		if (of_data && of_data->init)
2986d5fe107SBiju Das 			of_data->init(priv, of);
299f49cbe6bSDamien Riegel 	} else {
30002729c3dSFlorian Vaussard 		sp_populate(priv, pdata, res_mem->flags);
301f49cbe6bSDamien Riegel 	}
302986917b7SYegor Yefremov 
30300e4bbc8SJingoo Han 	platform_set_drvdata(pdev, dev);
304f534e52fSWolfgang Grandegger 	SET_NETDEV_DEV(dev, &pdev->dev);
305f534e52fSWolfgang Grandegger 
306f534e52fSWolfgang Grandegger 	err = register_sja1000dev(dev);
307f534e52fSWolfgang Grandegger 	if (err) {
308f534e52fSWolfgang Grandegger 		dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
309f534e52fSWolfgang Grandegger 			DRV_NAME, err);
310f534e52fSWolfgang Grandegger 		goto exit_free;
311f534e52fSWolfgang Grandegger 	}
312f534e52fSWolfgang Grandegger 
313255a9154SWolfgang Grandegger 	dev_info(&pdev->dev, "%s device registered (reg_base=%p, irq=%d)\n",
314255a9154SWolfgang Grandegger 		 DRV_NAME, priv->reg_base, dev->irq);
315f534e52fSWolfgang Grandegger 	return 0;
316f534e52fSWolfgang Grandegger 
317f534e52fSWolfgang Grandegger  exit_free:
318f534e52fSWolfgang Grandegger 	free_sja1000dev(dev);
319f534e52fSWolfgang Grandegger 	return err;
320f534e52fSWolfgang Grandegger }
321f534e52fSWolfgang Grandegger 
sp_remove(struct platform_device * pdev)322bc79adfbSUwe Kleine-König static void sp_remove(struct platform_device *pdev)
323f534e52fSWolfgang Grandegger {
32400e4bbc8SJingoo Han 	struct net_device *dev = platform_get_drvdata(pdev);
325f534e52fSWolfgang Grandegger 
326f534e52fSWolfgang Grandegger 	unregister_sja1000dev(dev);
327f534e52fSWolfgang Grandegger 	free_sja1000dev(dev);
328f534e52fSWolfgang Grandegger }
329f534e52fSWolfgang Grandegger 
330f534e52fSWolfgang Grandegger static struct platform_driver sp_driver = {
331f534e52fSWolfgang Grandegger 	.probe = sp_probe,
332*221013afSUwe Kleine-König 	.remove = sp_remove,
333f534e52fSWolfgang Grandegger 	.driver = {
334f534e52fSWolfgang Grandegger 		.name = DRV_NAME,
33502729c3dSFlorian Vaussard 		.of_match_table = sp_of_table,
336f534e52fSWolfgang Grandegger 	},
337f534e52fSWolfgang Grandegger };
338f534e52fSWolfgang Grandegger 
339871d3372SAxel Lin module_platform_driver(sp_driver);
340