xref: /linux/drivers/net/can/sja1000/sja1000_platform.c (revision 35e62ae830f950cfb09d2386412e09cc6d0e34b2)
1*35e62ae8SThomas 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>
17f534e52fSWolfgang Grandegger #include <linux/io.h>
1802729c3dSFlorian Vaussard #include <linux/of.h>
19f49cbe6bSDamien Riegel #include <linux/of_device.h>
2002729c3dSFlorian Vaussard #include <linux/of_irq.h>
21f534e52fSWolfgang Grandegger 
22f534e52fSWolfgang Grandegger #include "sja1000.h"
23f534e52fSWolfgang Grandegger 
24f534e52fSWolfgang Grandegger #define DRV_NAME "sja1000_platform"
2502729c3dSFlorian Vaussard #define SP_CAN_CLOCK  (16000000 / 2)
26f534e52fSWolfgang Grandegger 
27f534e52fSWolfgang Grandegger MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
2802729c3dSFlorian Vaussard MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
29f534e52fSWolfgang Grandegger MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus");
30d8c4386dSMarc Kleine-Budde MODULE_ALIAS("platform:" DRV_NAME);
31f534e52fSWolfgang Grandegger MODULE_LICENSE("GPL v2");
32f534e52fSWolfgang Grandegger 
33f49cbe6bSDamien Riegel struct sja1000_of_data {
34f49cbe6bSDamien Riegel 	size_t  priv_sz;
35f49cbe6bSDamien Riegel 	int     (*init)(struct sja1000_priv *priv, struct device_node *of);
36f49cbe6bSDamien Riegel };
37f49cbe6bSDamien Riegel 
38dfb86c0dSDamien Riegel struct technologic_priv {
39dfb86c0dSDamien Riegel 	spinlock_t      io_lock;
40dfb86c0dSDamien Riegel };
41dfb86c0dSDamien Riegel 
42986917b7SYegor Yefremov static u8 sp_read_reg8(const struct sja1000_priv *priv, int reg)
43f534e52fSWolfgang Grandegger {
44255a9154SWolfgang Grandegger 	return ioread8(priv->reg_base + reg);
45f534e52fSWolfgang Grandegger }
46f534e52fSWolfgang Grandegger 
47986917b7SYegor Yefremov static void sp_write_reg8(const struct sja1000_priv *priv, int reg, u8 val)
48f534e52fSWolfgang Grandegger {
49255a9154SWolfgang Grandegger 	iowrite8(val, priv->reg_base + reg);
50f534e52fSWolfgang Grandegger }
51f534e52fSWolfgang Grandegger 
52986917b7SYegor Yefremov static u8 sp_read_reg16(const struct sja1000_priv *priv, int reg)
53986917b7SYegor Yefremov {
54986917b7SYegor Yefremov 	return ioread8(priv->reg_base + reg * 2);
55986917b7SYegor Yefremov }
56986917b7SYegor Yefremov 
57986917b7SYegor Yefremov static void sp_write_reg16(const struct sja1000_priv *priv, int reg, u8 val)
58986917b7SYegor Yefremov {
59986917b7SYegor Yefremov 	iowrite8(val, priv->reg_base + reg * 2);
60986917b7SYegor Yefremov }
61986917b7SYegor Yefremov 
62986917b7SYegor Yefremov static u8 sp_read_reg32(const struct sja1000_priv *priv, int reg)
63986917b7SYegor Yefremov {
64986917b7SYegor Yefremov 	return ioread8(priv->reg_base + reg * 4);
65986917b7SYegor Yefremov }
66986917b7SYegor Yefremov 
67986917b7SYegor Yefremov static void sp_write_reg32(const struct sja1000_priv *priv, int reg, u8 val)
68986917b7SYegor Yefremov {
69986917b7SYegor Yefremov 	iowrite8(val, priv->reg_base + reg * 4);
70986917b7SYegor Yefremov }
71986917b7SYegor Yefremov 
72dfb86c0dSDamien Riegel static u8 sp_technologic_read_reg16(const struct sja1000_priv *priv, int reg)
73dfb86c0dSDamien Riegel {
74dfb86c0dSDamien Riegel 	struct technologic_priv *tp = priv->priv;
75dfb86c0dSDamien Riegel 	unsigned long flags;
76dfb86c0dSDamien Riegel 	u8 val;
77dfb86c0dSDamien Riegel 
78dfb86c0dSDamien Riegel 	spin_lock_irqsave(&tp->io_lock, flags);
79dfb86c0dSDamien Riegel 	iowrite16(reg, priv->reg_base + 0);
80dfb86c0dSDamien Riegel 	val = ioread16(priv->reg_base + 2);
81dfb86c0dSDamien Riegel 	spin_unlock_irqrestore(&tp->io_lock, flags);
82dfb86c0dSDamien Riegel 
83dfb86c0dSDamien Riegel 	return val;
84dfb86c0dSDamien Riegel }
85dfb86c0dSDamien Riegel 
86dfb86c0dSDamien Riegel static void sp_technologic_write_reg16(const struct sja1000_priv *priv,
87dfb86c0dSDamien Riegel 				       int reg, u8 val)
88dfb86c0dSDamien Riegel {
89dfb86c0dSDamien Riegel 	struct technologic_priv *tp = priv->priv;
90dfb86c0dSDamien Riegel 	unsigned long flags;
91dfb86c0dSDamien Riegel 
92dfb86c0dSDamien Riegel 	spin_lock_irqsave(&tp->io_lock, flags);
93dfb86c0dSDamien Riegel 	iowrite16(reg, priv->reg_base + 0);
94dfb86c0dSDamien Riegel 	iowrite16(val, priv->reg_base + 2);
95dfb86c0dSDamien Riegel 	spin_unlock_irqrestore(&tp->io_lock, flags);
96dfb86c0dSDamien Riegel }
97dfb86c0dSDamien Riegel 
98dfb86c0dSDamien Riegel static int sp_technologic_init(struct sja1000_priv *priv, struct device_node *of)
99dfb86c0dSDamien Riegel {
100dfb86c0dSDamien Riegel 	struct technologic_priv *tp = priv->priv;
101dfb86c0dSDamien Riegel 
102dfb86c0dSDamien Riegel 	priv->read_reg = sp_technologic_read_reg16;
103dfb86c0dSDamien Riegel 	priv->write_reg = sp_technologic_write_reg16;
104dfb86c0dSDamien Riegel 	spin_lock_init(&tp->io_lock);
105dfb86c0dSDamien Riegel 
106dfb86c0dSDamien Riegel 	return 0;
107dfb86c0dSDamien Riegel }
108dfb86c0dSDamien Riegel 
10902729c3dSFlorian Vaussard static void sp_populate(struct sja1000_priv *priv,
11002729c3dSFlorian Vaussard 			struct sja1000_platform_data *pdata,
11102729c3dSFlorian Vaussard 			unsigned long resource_mem_flags)
112f534e52fSWolfgang Grandegger {
11356e6943bSWolfgang Grandegger 	/* The CAN clock frequency is half the oscillator clock frequency */
11456e6943bSWolfgang Grandegger 	priv->can.clock.freq = pdata->osc_freq / 2;
115f534e52fSWolfgang Grandegger 	priv->ocr = pdata->ocr;
116f534e52fSWolfgang Grandegger 	priv->cdr = pdata->cdr;
117f534e52fSWolfgang Grandegger 
11802729c3dSFlorian Vaussard 	switch (resource_mem_flags & IORESOURCE_MEM_TYPE_MASK) {
119986917b7SYegor Yefremov 	case IORESOURCE_MEM_32BIT:
120986917b7SYegor Yefremov 		priv->read_reg = sp_read_reg32;
121986917b7SYegor Yefremov 		priv->write_reg = sp_write_reg32;
122986917b7SYegor Yefremov 		break;
123986917b7SYegor Yefremov 	case IORESOURCE_MEM_16BIT:
124986917b7SYegor Yefremov 		priv->read_reg = sp_read_reg16;
125986917b7SYegor Yefremov 		priv->write_reg = sp_write_reg16;
126986917b7SYegor Yefremov 		break;
127986917b7SYegor Yefremov 	case IORESOURCE_MEM_8BIT:
128986917b7SYegor Yefremov 	default:
129986917b7SYegor Yefremov 		priv->read_reg = sp_read_reg8;
130986917b7SYegor Yefremov 		priv->write_reg = sp_write_reg8;
131986917b7SYegor Yefremov 		break;
132986917b7SYegor Yefremov 	}
13302729c3dSFlorian Vaussard }
13402729c3dSFlorian Vaussard 
13502729c3dSFlorian Vaussard static void sp_populate_of(struct sja1000_priv *priv, struct device_node *of)
13602729c3dSFlorian Vaussard {
13702729c3dSFlorian Vaussard 	int err;
13802729c3dSFlorian Vaussard 	u32 prop;
13902729c3dSFlorian Vaussard 
140b18ec27cSFlorian Vaussard 	err = of_property_read_u32(of, "reg-io-width", &prop);
141b18ec27cSFlorian Vaussard 	if (err)
142b18ec27cSFlorian Vaussard 		prop = 1; /* 8 bit is default */
143b18ec27cSFlorian Vaussard 
144b18ec27cSFlorian Vaussard 	switch (prop) {
145b18ec27cSFlorian Vaussard 	case 4:
146b18ec27cSFlorian Vaussard 		priv->read_reg = sp_read_reg32;
147b18ec27cSFlorian Vaussard 		priv->write_reg = sp_write_reg32;
148b18ec27cSFlorian Vaussard 		break;
149b18ec27cSFlorian Vaussard 	case 2:
150b18ec27cSFlorian Vaussard 		priv->read_reg = sp_read_reg16;
151b18ec27cSFlorian Vaussard 		priv->write_reg = sp_write_reg16;
152b18ec27cSFlorian Vaussard 		break;
153b18ec27cSFlorian Vaussard 	case 1:	/* fallthrough */
154b18ec27cSFlorian Vaussard 	default:
15502729c3dSFlorian Vaussard 		priv->read_reg = sp_read_reg8;
15602729c3dSFlorian Vaussard 		priv->write_reg = sp_write_reg8;
157b18ec27cSFlorian Vaussard 	}
15802729c3dSFlorian Vaussard 
15902729c3dSFlorian Vaussard 	err = of_property_read_u32(of, "nxp,external-clock-frequency", &prop);
16002729c3dSFlorian Vaussard 	if (!err)
16102729c3dSFlorian Vaussard 		priv->can.clock.freq = prop / 2;
16202729c3dSFlorian Vaussard 	else
16302729c3dSFlorian Vaussard 		priv->can.clock.freq = SP_CAN_CLOCK; /* default */
16402729c3dSFlorian Vaussard 
16502729c3dSFlorian Vaussard 	err = of_property_read_u32(of, "nxp,tx-output-mode", &prop);
16602729c3dSFlorian Vaussard 	if (!err)
16702729c3dSFlorian Vaussard 		priv->ocr |= prop & OCR_MODE_MASK;
16802729c3dSFlorian Vaussard 	else
16902729c3dSFlorian Vaussard 		priv->ocr |= OCR_MODE_NORMAL; /* default */
17002729c3dSFlorian Vaussard 
17102729c3dSFlorian Vaussard 	err = of_property_read_u32(of, "nxp,tx-output-config", &prop);
17202729c3dSFlorian Vaussard 	if (!err)
17302729c3dSFlorian Vaussard 		priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK;
17402729c3dSFlorian Vaussard 	else
17502729c3dSFlorian Vaussard 		priv->ocr |= OCR_TX0_PULLDOWN; /* default */
17602729c3dSFlorian Vaussard 
17702729c3dSFlorian Vaussard 	err = of_property_read_u32(of, "nxp,clock-out-frequency", &prop);
17802729c3dSFlorian Vaussard 	if (!err && prop) {
17902729c3dSFlorian Vaussard 		u32 divider = priv->can.clock.freq * 2 / prop;
18002729c3dSFlorian Vaussard 
18102729c3dSFlorian Vaussard 		if (divider > 1)
18202729c3dSFlorian Vaussard 			priv->cdr |= divider / 2 - 1;
18302729c3dSFlorian Vaussard 		else
18402729c3dSFlorian Vaussard 			priv->cdr |= CDR_CLKOUT_MASK;
18502729c3dSFlorian Vaussard 	} else {
18602729c3dSFlorian Vaussard 		priv->cdr |= CDR_CLK_OFF; /* default */
18702729c3dSFlorian Vaussard 	}
18802729c3dSFlorian Vaussard 
18902729c3dSFlorian Vaussard 	if (!of_property_read_bool(of, "nxp,no-comparator-bypass"))
19002729c3dSFlorian Vaussard 		priv->cdr |= CDR_CBP; /* default */
19102729c3dSFlorian Vaussard }
19202729c3dSFlorian Vaussard 
193dfb86c0dSDamien Riegel static struct sja1000_of_data technologic_data = {
194dfb86c0dSDamien Riegel 	.priv_sz = sizeof(struct technologic_priv),
195dfb86c0dSDamien Riegel 	.init = sp_technologic_init,
196dfb86c0dSDamien Riegel };
197dfb86c0dSDamien Riegel 
198f49cbe6bSDamien Riegel static const struct of_device_id sp_of_table[] = {
199f49cbe6bSDamien Riegel 	{ .compatible = "nxp,sja1000", .data = NULL, },
200dfb86c0dSDamien Riegel 	{ .compatible = "technologic,sja1000", .data = &technologic_data, },
201f49cbe6bSDamien Riegel 	{ /* sentinel */ },
202f49cbe6bSDamien Riegel };
203f49cbe6bSDamien Riegel MODULE_DEVICE_TABLE(of, sp_of_table);
204f49cbe6bSDamien Riegel 
20502729c3dSFlorian Vaussard static int sp_probe(struct platform_device *pdev)
20602729c3dSFlorian Vaussard {
20702729c3dSFlorian Vaussard 	int err, irq = 0;
20802729c3dSFlorian Vaussard 	void __iomem *addr;
20902729c3dSFlorian Vaussard 	struct net_device *dev;
21002729c3dSFlorian Vaussard 	struct sja1000_priv *priv;
21102729c3dSFlorian Vaussard 	struct resource *res_mem, *res_irq = NULL;
21202729c3dSFlorian Vaussard 	struct sja1000_platform_data *pdata;
21302729c3dSFlorian Vaussard 	struct device_node *of = pdev->dev.of_node;
214f49cbe6bSDamien Riegel 	const struct of_device_id *of_id;
215f49cbe6bSDamien Riegel 	const struct sja1000_of_data *of_data = NULL;
216f49cbe6bSDamien Riegel 	size_t priv_sz = 0;
21702729c3dSFlorian Vaussard 
21802729c3dSFlorian Vaussard 	pdata = dev_get_platdata(&pdev->dev);
21902729c3dSFlorian Vaussard 	if (!pdata && !of) {
22002729c3dSFlorian Vaussard 		dev_err(&pdev->dev, "No platform data provided!\n");
22102729c3dSFlorian Vaussard 		return -ENODEV;
22202729c3dSFlorian Vaussard 	}
22302729c3dSFlorian Vaussard 
22402729c3dSFlorian Vaussard 	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
22502729c3dSFlorian Vaussard 	if (!res_mem)
22602729c3dSFlorian Vaussard 		return -ENODEV;
22702729c3dSFlorian Vaussard 
22802729c3dSFlorian Vaussard 	if (!devm_request_mem_region(&pdev->dev, res_mem->start,
22902729c3dSFlorian Vaussard 				     resource_size(res_mem), DRV_NAME))
23002729c3dSFlorian Vaussard 		return -EBUSY;
23102729c3dSFlorian Vaussard 
23202729c3dSFlorian Vaussard 	addr = devm_ioremap_nocache(&pdev->dev, res_mem->start,
23302729c3dSFlorian Vaussard 				    resource_size(res_mem));
23402729c3dSFlorian Vaussard 	if (!addr)
23502729c3dSFlorian Vaussard 		return -ENOMEM;
23602729c3dSFlorian Vaussard 
23702729c3dSFlorian Vaussard 	if (of)
23802729c3dSFlorian Vaussard 		irq = irq_of_parse_and_map(of, 0);
23902729c3dSFlorian Vaussard 	else
24002729c3dSFlorian Vaussard 		res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
24102729c3dSFlorian Vaussard 
24202729c3dSFlorian Vaussard 	if (!irq && !res_irq)
24302729c3dSFlorian Vaussard 		return -ENODEV;
24402729c3dSFlorian Vaussard 
245f49cbe6bSDamien Riegel 	of_id = of_match_device(sp_of_table, &pdev->dev);
246f49cbe6bSDamien Riegel 	if (of_id && of_id->data) {
247f49cbe6bSDamien Riegel 		of_data = of_id->data;
248f49cbe6bSDamien Riegel 		priv_sz = of_data->priv_sz;
249f49cbe6bSDamien Riegel 	}
250f49cbe6bSDamien Riegel 
251f49cbe6bSDamien Riegel 	dev = alloc_sja1000dev(priv_sz);
25202729c3dSFlorian Vaussard 	if (!dev)
25302729c3dSFlorian Vaussard 		return -ENOMEM;
25402729c3dSFlorian Vaussard 	priv = netdev_priv(dev);
25502729c3dSFlorian Vaussard 
25602729c3dSFlorian Vaussard 	if (res_irq) {
25702729c3dSFlorian Vaussard 		irq = res_irq->start;
25802729c3dSFlorian Vaussard 		priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
25902729c3dSFlorian Vaussard 		if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
26002729c3dSFlorian Vaussard 			priv->irq_flags |= IRQF_SHARED;
26102729c3dSFlorian Vaussard 	} else {
26202729c3dSFlorian Vaussard 		priv->irq_flags = IRQF_SHARED;
26302729c3dSFlorian Vaussard 	}
26402729c3dSFlorian Vaussard 
26502729c3dSFlorian Vaussard 	dev->irq = irq;
26602729c3dSFlorian Vaussard 	priv->reg_base = addr;
26702729c3dSFlorian Vaussard 
268f49cbe6bSDamien Riegel 	if (of) {
26902729c3dSFlorian Vaussard 		sp_populate_of(priv, of);
270f49cbe6bSDamien Riegel 
271f49cbe6bSDamien Riegel 		if (of_data && of_data->init) {
272f49cbe6bSDamien Riegel 			err = of_data->init(priv, of);
273f49cbe6bSDamien Riegel 			if (err)
274f49cbe6bSDamien Riegel 				goto exit_free;
275f49cbe6bSDamien Riegel 		}
276f49cbe6bSDamien Riegel 	} else {
27702729c3dSFlorian Vaussard 		sp_populate(priv, pdata, res_mem->flags);
278f49cbe6bSDamien Riegel 	}
279986917b7SYegor Yefremov 
28000e4bbc8SJingoo Han 	platform_set_drvdata(pdev, dev);
281f534e52fSWolfgang Grandegger 	SET_NETDEV_DEV(dev, &pdev->dev);
282f534e52fSWolfgang Grandegger 
283f534e52fSWolfgang Grandegger 	err = register_sja1000dev(dev);
284f534e52fSWolfgang Grandegger 	if (err) {
285f534e52fSWolfgang Grandegger 		dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
286f534e52fSWolfgang Grandegger 			DRV_NAME, err);
287f534e52fSWolfgang Grandegger 		goto exit_free;
288f534e52fSWolfgang Grandegger 	}
289f534e52fSWolfgang Grandegger 
290255a9154SWolfgang Grandegger 	dev_info(&pdev->dev, "%s device registered (reg_base=%p, irq=%d)\n",
291255a9154SWolfgang Grandegger 		 DRV_NAME, priv->reg_base, dev->irq);
292f534e52fSWolfgang Grandegger 	return 0;
293f534e52fSWolfgang Grandegger 
294f534e52fSWolfgang Grandegger  exit_free:
295f534e52fSWolfgang Grandegger 	free_sja1000dev(dev);
296f534e52fSWolfgang Grandegger 	return err;
297f534e52fSWolfgang Grandegger }
298f534e52fSWolfgang Grandegger 
299f534e52fSWolfgang Grandegger static int sp_remove(struct platform_device *pdev)
300f534e52fSWolfgang Grandegger {
30100e4bbc8SJingoo Han 	struct net_device *dev = platform_get_drvdata(pdev);
302f534e52fSWolfgang Grandegger 
303f534e52fSWolfgang Grandegger 	unregister_sja1000dev(dev);
304f534e52fSWolfgang Grandegger 	free_sja1000dev(dev);
305f534e52fSWolfgang Grandegger 
306f534e52fSWolfgang Grandegger 	return 0;
307f534e52fSWolfgang Grandegger }
308f534e52fSWolfgang Grandegger 
309f534e52fSWolfgang Grandegger static struct platform_driver sp_driver = {
310f534e52fSWolfgang Grandegger 	.probe = sp_probe,
311f534e52fSWolfgang Grandegger 	.remove = sp_remove,
312f534e52fSWolfgang Grandegger 	.driver = {
313f534e52fSWolfgang Grandegger 		.name = DRV_NAME,
31402729c3dSFlorian Vaussard 		.of_match_table = sp_of_table,
315f534e52fSWolfgang Grandegger 	},
316f534e52fSWolfgang Grandegger };
317f534e52fSWolfgang Grandegger 
318871d3372SAxel Lin module_platform_driver(sp_driver);
319