Commit | Line | Data |
---|---|---|
f524f829 DM |
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/platform_device.h> | |
9 | ||
10 | #include "m_can.h" | |
11 | ||
12 | struct m_can_plat_priv { | |
13 | void __iomem *base; | |
14 | void __iomem *mram_base; | |
15 | }; | |
16 | ||
17 | static u32 iomap_read_reg(struct m_can_priv *cdev, int reg) | |
18 | { | |
19 | struct m_can_plat_priv *priv = | |
20 | (struct m_can_plat_priv *)cdev->device_data; | |
21 | ||
22 | return readl(priv->base + reg); | |
23 | } | |
24 | ||
25 | static u32 iomap_read_fifo(struct m_can_priv *cdev, int offset) | |
26 | { | |
27 | struct m_can_plat_priv *priv = | |
28 | (struct m_can_plat_priv *)cdev->device_data; | |
29 | ||
30 | return readl(priv->mram_base + offset); | |
31 | } | |
32 | ||
33 | static int iomap_write_reg(struct m_can_priv *cdev, int reg, int val) | |
34 | { | |
35 | struct m_can_plat_priv *priv = | |
36 | (struct m_can_plat_priv *)cdev->device_data; | |
37 | ||
38 | writel(val, priv->base + reg); | |
39 | ||
40 | return 0; | |
41 | } | |
42 | ||
43 | static int iomap_write_fifo(struct m_can_priv *cdev, int offset, int val) | |
44 | { | |
45 | struct m_can_plat_priv *priv = | |
46 | (struct m_can_plat_priv *)cdev->device_data; | |
47 | ||
48 | writel(val, priv->mram_base + offset); | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
53 | static struct m_can_ops m_can_plat_ops = { | |
54 | .read_reg = iomap_read_reg, | |
55 | .write_reg = iomap_write_reg, | |
56 | .write_fifo = iomap_write_fifo, | |
57 | .read_fifo = iomap_read_fifo, | |
58 | }; | |
59 | ||
60 | static int m_can_plat_probe(struct platform_device *pdev) | |
61 | { | |
62 | struct m_can_priv *mcan_class; | |
63 | struct m_can_plat_priv *priv; | |
64 | struct resource *res; | |
65 | void __iomem *addr; | |
66 | void __iomem *mram_addr; | |
67 | int irq, ret = 0; | |
68 | ||
69 | mcan_class = m_can_class_allocate_dev(&pdev->dev); | |
70 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | |
71 | if (!priv) | |
72 | return -ENOMEM; | |
73 | ||
74 | mcan_class->device_data = priv; | |
75 | ||
76 | m_can_class_get_clocks(mcan_class); | |
77 | ||
78 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); | |
79 | addr = devm_ioremap_resource(&pdev->dev, res); | |
80 | irq = platform_get_irq_byname(pdev, "int0"); | |
81 | if (IS_ERR(addr) || irq < 0) { | |
82 | ret = -EINVAL; | |
83 | goto failed_ret; | |
84 | } | |
85 | ||
86 | /* message ram could be shared */ | |
87 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); | |
88 | if (!res) { | |
89 | ret = -ENODEV; | |
90 | goto failed_ret; | |
91 | } | |
92 | ||
93 | mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | |
94 | if (!mram_addr) { | |
95 | ret = -ENOMEM; | |
96 | goto failed_ret; | |
97 | } | |
98 | ||
99 | priv->base = addr; | |
100 | priv->mram_base = mram_addr; | |
101 | ||
102 | mcan_class->net->irq = irq; | |
103 | mcan_class->pm_clock_support = 1; | |
104 | mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk); | |
105 | mcan_class->dev = &pdev->dev; | |
106 | ||
107 | mcan_class->ops = &m_can_plat_ops; | |
108 | ||
109 | mcan_class->is_peripheral = false; | |
110 | ||
111 | platform_set_drvdata(pdev, mcan_class->dev); | |
112 | ||
113 | m_can_init_ram(mcan_class); | |
114 | ||
115 | ret = m_can_class_register(mcan_class); | |
116 | ||
117 | failed_ret: | |
118 | return ret; | |
119 | } | |
120 | ||
121 | static __maybe_unused int m_can_suspend(struct device *dev) | |
122 | { | |
123 | return m_can_class_suspend(dev); | |
124 | } | |
125 | ||
126 | static __maybe_unused int m_can_resume(struct device *dev) | |
127 | { | |
128 | return m_can_class_resume(dev); | |
129 | } | |
130 | ||
131 | static int m_can_plat_remove(struct platform_device *pdev) | |
132 | { | |
133 | struct net_device *dev = platform_get_drvdata(pdev); | |
134 | struct m_can_priv *mcan_class = netdev_priv(dev); | |
135 | ||
136 | m_can_class_unregister(mcan_class); | |
137 | ||
138 | platform_set_drvdata(pdev, NULL); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | static int __maybe_unused m_can_runtime_suspend(struct device *dev) | |
144 | { | |
145 | struct net_device *ndev = dev_get_drvdata(dev); | |
146 | struct m_can_priv *mcan_class = netdev_priv(ndev); | |
147 | ||
148 | m_can_class_suspend(dev); | |
149 | ||
150 | clk_disable_unprepare(mcan_class->cclk); | |
151 | clk_disable_unprepare(mcan_class->hclk); | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | static int __maybe_unused m_can_runtime_resume(struct device *dev) | |
157 | { | |
158 | struct net_device *ndev = dev_get_drvdata(dev); | |
159 | struct m_can_priv *mcan_class = netdev_priv(ndev); | |
160 | int err; | |
161 | ||
162 | err = clk_prepare_enable(mcan_class->hclk); | |
163 | if (err) | |
164 | return err; | |
165 | ||
166 | err = clk_prepare_enable(mcan_class->cclk); | |
167 | if (err) | |
168 | clk_disable_unprepare(mcan_class->hclk); | |
169 | ||
170 | m_can_class_resume(dev); | |
171 | ||
172 | return err; | |
173 | } | |
174 | ||
175 | static const struct dev_pm_ops m_can_pmops = { | |
176 | SET_RUNTIME_PM_OPS(m_can_runtime_suspend, | |
177 | m_can_runtime_resume, NULL) | |
178 | SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume) | |
179 | }; | |
180 | ||
181 | static const struct of_device_id m_can_of_table[] = { | |
182 | { .compatible = "bosch,m_can", .data = NULL }, | |
183 | { /* sentinel */ }, | |
184 | }; | |
185 | MODULE_DEVICE_TABLE(of, m_can_of_table); | |
186 | ||
187 | static struct platform_driver m_can_plat_driver = { | |
188 | .driver = { | |
189 | .name = KBUILD_MODNAME, | |
190 | .of_match_table = m_can_of_table, | |
191 | .pm = &m_can_pmops, | |
192 | }, | |
193 | .probe = m_can_plat_probe, | |
194 | .remove = m_can_plat_remove, | |
195 | }; | |
196 | ||
197 | module_platform_driver(m_can_plat_driver); | |
198 | ||
199 | MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>"); | |
200 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); | |
201 | MODULE_LICENSE("GPL v2"); | |
202 | MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers"); |