Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0
  2// IOMapped CAN bus driver for Bosch M_CAN controller
  3// Copyright (C) 2014 Freescale Semiconductor, Inc.
  4//	Dong Aisheng <b29396@freescale.com>
  5//
  6// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
  7
  8#include <linux/phy/phy.h>
  9#include <linux/platform_device.h>
 10
 11#include "m_can.h"
 12
 13struct m_can_plat_priv {
 14	struct m_can_classdev cdev;
 15
 16	void __iomem *base;
 17	void __iomem *mram_base;
 18};
 19
 20static inline struct m_can_plat_priv *cdev_to_priv(struct m_can_classdev *cdev)
 21{
 22	return container_of(cdev, struct m_can_plat_priv, cdev);
 23}
 24
 25static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg)
 26{
 27	struct m_can_plat_priv *priv = cdev_to_priv(cdev);
 28
 29	return readl(priv->base + reg);
 30}
 31
 32static int iomap_read_fifo(struct m_can_classdev *cdev, int offset, void *val, size_t val_count)
 33{
 34	struct m_can_plat_priv *priv = cdev_to_priv(cdev);
 35	void __iomem *src = priv->mram_base + offset;
 36
 37	while (val_count--) {
 38		*(unsigned int *)val = ioread32(src);
 39		val += 4;
 40		src += 4;
 41	}
 42
 43	return 0;
 44}
 45
 46static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
 47{
 48	struct m_can_plat_priv *priv = cdev_to_priv(cdev);
 49
 50	writel(val, priv->base + reg);
 51
 52	return 0;
 53}
 54
 55static int iomap_write_fifo(struct m_can_classdev *cdev, int offset,
 56			    const void *val, size_t val_count)
 57{
 58	struct m_can_plat_priv *priv = cdev_to_priv(cdev);
 59	void __iomem *dst = priv->mram_base + offset;
 60
 61	while (val_count--) {
 62		iowrite32(*(unsigned int *)val, dst);
 63		val += 4;
 64		dst += 4;
 65	}
 66
 67	return 0;
 68}
 69
 70static struct m_can_ops m_can_plat_ops = {
 71	.read_reg = iomap_read_reg,
 72	.write_reg = iomap_write_reg,
 73	.write_fifo = iomap_write_fifo,
 74	.read_fifo = iomap_read_fifo,
 75};
 76
 77static int m_can_plat_probe(struct platform_device *pdev)
 78{
 79	struct m_can_classdev *mcan_class;
 80	struct m_can_plat_priv *priv;
 81	struct resource *res;
 82	void __iomem *addr;
 83	void __iomem *mram_addr;
 84	struct phy *transceiver;
 85	int irq, ret = 0;
 86
 87	mcan_class = m_can_class_allocate_dev(&pdev->dev,
 88					      sizeof(struct m_can_plat_priv));
 89	if (!mcan_class)
 90		return -ENOMEM;
 91
 92	priv = cdev_to_priv(mcan_class);
 93
 94	ret = m_can_class_get_clocks(mcan_class);
 95	if (ret)
 96		goto probe_fail;
 97
 98	addr = devm_platform_ioremap_resource_byname(pdev, "m_can");
 99	irq = platform_get_irq_byname(pdev, "int0");
100	if (IS_ERR(addr) || irq < 0) {
101		ret = -EINVAL;
102		goto probe_fail;
103	}
104
105	/* message ram could be shared */
106	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
107	if (!res) {
108		ret = -ENODEV;
109		goto probe_fail;
110	}
111
112	mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
113	if (!mram_addr) {
114		ret = -ENOMEM;
115		goto probe_fail;
116	}
117
118	transceiver = devm_phy_optional_get(&pdev->dev, NULL);
119	if (IS_ERR(transceiver)) {
120		ret = PTR_ERR(transceiver);
121		dev_err_probe(&pdev->dev, ret, "failed to get phy\n");
122		goto probe_fail;
123	}
124
125	if (transceiver)
126		mcan_class->can.bitrate_max = transceiver->attrs.max_link_rate;
127
128	priv->base = addr;
129	priv->mram_base = mram_addr;
130
131	mcan_class->net->irq = irq;
132	mcan_class->pm_clock_support = 1;
133	mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk);
134	mcan_class->dev = &pdev->dev;
135	mcan_class->transceiver = transceiver;
136
137	mcan_class->ops = &m_can_plat_ops;
138
139	mcan_class->is_peripheral = false;
140
141	platform_set_drvdata(pdev, mcan_class);
142
143	pm_runtime_enable(mcan_class->dev);
144	ret = m_can_class_register(mcan_class);
145	if (ret)
146		goto out_runtime_disable;
147
148	return ret;
149
150out_runtime_disable:
151	pm_runtime_disable(mcan_class->dev);
152probe_fail:
153	m_can_class_free_dev(mcan_class->net);
154	return ret;
155}
156
157static __maybe_unused int m_can_suspend(struct device *dev)
158{
159	return m_can_class_suspend(dev);
160}
161
162static __maybe_unused int m_can_resume(struct device *dev)
163{
164	return m_can_class_resume(dev);
165}
166
167static int m_can_plat_remove(struct platform_device *pdev)
168{
169	struct m_can_plat_priv *priv = platform_get_drvdata(pdev);
170	struct m_can_classdev *mcan_class = &priv->cdev;
171
172	m_can_class_unregister(mcan_class);
173
174	m_can_class_free_dev(mcan_class->net);
175
176	return 0;
177}
178
179static int __maybe_unused m_can_runtime_suspend(struct device *dev)
180{
181	struct m_can_plat_priv *priv = dev_get_drvdata(dev);
182	struct m_can_classdev *mcan_class = &priv->cdev;
183
184	clk_disable_unprepare(mcan_class->cclk);
185	clk_disable_unprepare(mcan_class->hclk);
186
187	return 0;
188}
189
190static int __maybe_unused m_can_runtime_resume(struct device *dev)
191{
192	struct m_can_plat_priv *priv = dev_get_drvdata(dev);
193	struct m_can_classdev *mcan_class = &priv->cdev;
194	int err;
195
196	err = clk_prepare_enable(mcan_class->hclk);
197	if (err)
198		return err;
199
200	err = clk_prepare_enable(mcan_class->cclk);
201	if (err)
202		clk_disable_unprepare(mcan_class->hclk);
203
204	return err;
205}
206
207static const struct dev_pm_ops m_can_pmops = {
208	SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
209			   m_can_runtime_resume, NULL)
210	SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
211};
212
213static const struct of_device_id m_can_of_table[] = {
214	{ .compatible = "bosch,m_can", .data = NULL },
215	{ /* sentinel */ },
216};
217MODULE_DEVICE_TABLE(of, m_can_of_table);
218
219static struct platform_driver m_can_plat_driver = {
220	.driver = {
221		.name = KBUILD_MODNAME,
222		.of_match_table = m_can_of_table,
223		.pm     = &m_can_pmops,
224	},
225	.probe = m_can_plat_probe,
226	.remove = m_can_plat_remove,
227};
228
229module_platform_driver(m_can_plat_driver);
230
231MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
232MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
233MODULE_LICENSE("GPL v2");
234MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers");