c_can_platform.c 9.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
 * Platform CAN bus driver for Bosch C_CAN controller
 *
 * Copyright (C) 2010 ST Microelectronics
 * Bhupesh Sharma <bhupesh.sharma@st.com>
 *
 * Borrowed heavily from the C_CAN driver originally written by:
 * Copyright (C) 2007
 * - Sascha Hauer, Marc Kleine-Budde, Pengutronix <s.hauer@pengutronix.de>
 * - Simon Kallweit, intefo AG <simon.kallweit@intefo.ch>
 *
 * Bosch C_CAN controller is compliant to CAN protocol version 2.0 part A and B.
 * Bosch C_CAN user manual can be obtained from:
 * http://www.semiconductors.bosch.de/media/en/pdf/ipmodules_1/c_can/
 * users_manual_c_can.pdf
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
33 34
#include <linux/of.h>
#include <linux/of_device.h>
35 36 37 38 39

#include <linux/can/dev.h>

#include "c_can.h"

40 41 42 43
#define CAN_RAMINIT_START_MASK(i)	(0x001 << (i))
#define CAN_RAMINIT_DONE_MASK(i)	(0x100 << (i))
#define CAN_RAMINIT_ALL_MASK(i)		(0x101 << (i))
static DEFINE_SPINLOCK(raminit_lock);
44 45 46 47 48 49 50
/*
 * 16-bit c_can registers can be arranged differently in the memory
 * architecture of different implementations. For example: 16-bit
 * registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
 * Handle the same by providing a common read/write interface.
 */
static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv,
51
						enum reg index)
52
{
53
	return readw(priv->base + priv->regs[index]);
54 55 56
}

static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv,
57
						enum reg index, u16 val)
58
{
59
	writew(val, priv->base + priv->regs[index]);
60 61 62
}

static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv,
63
						enum reg index)
64
{
65
	return readw(priv->base + 2 * priv->regs[index]);
66 67 68
}

static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
69
						enum reg index, u16 val)
70
{
71
	writew(val, priv->base + 2 * priv->regs[index]);
72 73
}

74 75 76 77 78 79 80 81 82
static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask,
				  u32 val)
{
	/* We look only at the bits of our instance. */
	val &= mask;
	while ((readl(priv->raminit_ctrlreg) & mask) != val)
		udelay(1);
}

83 84
static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
{
85 86
	u32 mask = CAN_RAMINIT_ALL_MASK(priv->instance);
	u32 ctrl;
87

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	spin_lock(&raminit_lock);

	ctrl = readl(priv->raminit_ctrlreg);
	/* We clear the done and start bit first. The start bit is
	 * looking at the 0 -> transition, but is not self clearing;
	 * And we clear the init done bit as well.
	 */
	ctrl &= ~CAN_RAMINIT_START_MASK(priv->instance);
	ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
	writel(ctrl, priv->raminit_ctrlreg);
	ctrl &= ~CAN_RAMINIT_DONE_MASK(priv->instance);
	c_can_hw_raminit_wait(priv, ctrl, mask);

	if (enable) {
		/* Set start bit and wait for the done bit. */
		ctrl |= CAN_RAMINIT_START_MASK(priv->instance);
		writel(ctrl, priv->raminit_ctrlreg);
		ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
		c_can_hw_raminit_wait(priv, ctrl, mask);
	}
	spin_unlock(&raminit_lock);
109 110
}

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
static struct platform_device_id c_can_id_table[] = {
	[BOSCH_C_CAN_PLATFORM] = {
		.name = KBUILD_MODNAME,
		.driver_data = BOSCH_C_CAN,
	},
	[BOSCH_C_CAN] = {
		.name = "c_can",
		.driver_data = BOSCH_C_CAN,
	},
	[BOSCH_D_CAN] = {
		.name = "d_can",
		.driver_data = BOSCH_D_CAN,
	}, {
	}
};
126
MODULE_DEVICE_TABLE(platform, c_can_id_table);
127 128 129 130 131 132

static const struct of_device_id c_can_of_table[] = {
	{ .compatible = "bosch,c_can", .data = &c_can_id_table[BOSCH_C_CAN] },
	{ .compatible = "bosch,d_can", .data = &c_can_id_table[BOSCH_D_CAN] },
	{ /* sentinel */ },
};
133
MODULE_DEVICE_TABLE(of, c_can_of_table);
134

B
Bill Pemberton 已提交
135
static int c_can_plat_probe(struct platform_device *pdev)
136 137 138 139 140
{
	int ret;
	void __iomem *addr;
	struct net_device *dev;
	struct c_can_priv *priv;
141
	const struct of_device_id *match;
142
	const struct platform_device_id *id;
143
	struct resource *mem, *res;
144
	int irq;
145 146
	struct clk *clk;

147 148 149 150 151 152 153 154 155 156 157 158
	if (pdev->dev.of_node) {
		match = of_match_device(c_can_of_table, &pdev->dev);
		if (!match) {
			dev_err(&pdev->dev, "Failed to find matching dt id\n");
			ret = -EINVAL;
			goto exit;
		}
		id = match->data;
	} else {
		id = platform_get_device_id(pdev);
	}

159 160 161 162 163 164 165 166 167 168
	/* get the appropriate clk */
	clk = clk_get(&pdev->dev, NULL);
	if (IS_ERR(clk)) {
		dev_err(&pdev->dev, "no clock defined\n");
		ret = -ENODEV;
		goto exit;
	}

	/* get the platform data */
	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
169 170
	irq = platform_get_irq(pdev, 0);
	if (!mem || irq <= 0) {
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
		ret = -ENODEV;
		goto exit_free_clk;
	}

	if (!request_mem_region(mem->start, resource_size(mem),
				KBUILD_MODNAME)) {
		dev_err(&pdev->dev, "resource unavailable\n");
		ret = -ENODEV;
		goto exit_free_clk;
	}

	addr = ioremap(mem->start, resource_size(mem));
	if (!addr) {
		dev_err(&pdev->dev, "failed to map can port\n");
		ret = -ENOMEM;
		goto exit_release_mem;
	}

	/* allocate the c_can device */
	dev = alloc_c_can_dev();
	if (!dev) {
		ret = -ENOMEM;
		goto exit_iounmap;
	}

	priv = netdev_priv(dev);
197
	switch (id->driver_data) {
198
	case BOSCH_C_CAN:
199 200 201 202 203 204 205 206 207 208 209 210 211
		priv->regs = reg_map_c_can;
		switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
		case IORESOURCE_MEM_32BIT:
			priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
			priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
			break;
		case IORESOURCE_MEM_16BIT:
		default:
			priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
			priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
			break;
		}
		break;
212
	case BOSCH_D_CAN:
213 214 215 216
		priv->regs = reg_map_d_can;
		priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
		priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
		priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
217 218 219 220 221 222 223

		if (pdev->dev.of_node)
			priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can");
		else
			priv->instance = pdev->id;

		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
224
		priv->raminit_ctrlreg = devm_ioremap_resource(&pdev->dev, res);
225
		if (IS_ERR(priv->raminit_ctrlreg) || priv->instance < 0)
226 227 228
			dev_info(&pdev->dev, "control memory is not used for raminit\n");
		else
			priv->raminit = c_can_hw_raminit;
229 230 231 232 233
		break;
	default:
		ret = -EINVAL;
		goto exit_free_device;
	}
234

235
	dev->irq = irq;
236
	priv->base = addr;
237
	priv->device = &pdev->dev;
238 239
	priv->can.clock.freq = clk_get_rate(clk);
	priv->priv = clk;
240
	priv->type = id->driver_data;
241 242 243 244 245 246 247 248 249 250 251 252

	platform_set_drvdata(pdev, dev);
	SET_NETDEV_DEV(dev, &pdev->dev);

	ret = register_c_can_dev(dev);
	if (ret) {
		dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
			KBUILD_MODNAME, ret);
		goto exit_free_device;
	}

	dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n",
253
		 KBUILD_MODNAME, priv->base, dev->irq);
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
	return 0;

exit_free_device:
	free_c_can_dev(dev);
exit_iounmap:
	iounmap(addr);
exit_release_mem:
	release_mem_region(mem->start, resource_size(mem));
exit_free_clk:
	clk_put(clk);
exit:
	dev_err(&pdev->dev, "probe failed\n");

	return ret;
}

B
Bill Pemberton 已提交
270
static int c_can_plat_remove(struct platform_device *pdev)
271 272 273 274 275 276 277 278
{
	struct net_device *dev = platform_get_drvdata(pdev);
	struct c_can_priv *priv = netdev_priv(dev);
	struct resource *mem;

	unregister_c_can_dev(dev);

	free_c_can_dev(dev);
279
	iounmap(priv->base);
280 281 282 283 284 285 286 287 288

	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	release_mem_region(mem->start, resource_size(mem));

	clk_put(priv->priv);

	return 0;
}

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
#ifdef CONFIG_PM
static int c_can_suspend(struct platform_device *pdev, pm_message_t state)
{
	int ret;
	struct net_device *ndev = platform_get_drvdata(pdev);
	struct c_can_priv *priv = netdev_priv(ndev);

	if (priv->type != BOSCH_D_CAN) {
		dev_warn(&pdev->dev, "Not supported\n");
		return 0;
	}

	if (netif_running(ndev)) {
		netif_stop_queue(ndev);
		netif_device_detach(ndev);
	}

	ret = c_can_power_down(ndev);
	if (ret) {
		netdev_err(ndev, "failed to enter power down mode\n");
		return ret;
	}

	priv->can.state = CAN_STATE_SLEEPING;

	return 0;
}

static int c_can_resume(struct platform_device *pdev)
{
	int ret;
	struct net_device *ndev = platform_get_drvdata(pdev);
	struct c_can_priv *priv = netdev_priv(ndev);

	if (priv->type != BOSCH_D_CAN) {
		dev_warn(&pdev->dev, "Not supported\n");
		return 0;
	}

	ret = c_can_power_up(ndev);
	if (ret) {
		netdev_err(ndev, "Still in power down mode\n");
		return ret;
	}

	priv->can.state = CAN_STATE_ERROR_ACTIVE;

	if (netif_running(ndev)) {
		netif_device_attach(ndev);
		netif_start_queue(ndev);
	}

	return 0;
}
#else
#define c_can_suspend NULL
#define c_can_resume NULL
#endif

348 349 350 351
static struct platform_driver c_can_plat_driver = {
	.driver = {
		.name = KBUILD_MODNAME,
		.owner = THIS_MODULE,
352
		.of_match_table = c_can_of_table,
353 354
	},
	.probe = c_can_plat_probe,
B
Bill Pemberton 已提交
355
	.remove = c_can_plat_remove,
356 357
	.suspend = c_can_suspend,
	.resume = c_can_resume,
358
	.id_table = c_can_id_table,
359 360
};

361
module_platform_driver(c_can_plat_driver);
362 363 364 365

MODULE_AUTHOR("Bhupesh Sharma <bhupesh.sharma@st.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Platform CAN bus driver for Bosch C_CAN controller");