1e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger/*
2e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * Driver for CC770 and AN82527 CAN controllers on the platform bus
3e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *
4e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * Copyright (C) 2009, 2011 Wolfgang Grandegger <wg@grandegger.com>
5e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *
6e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * This program is free software; you can redistribute it and/or modify
7e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * it under the terms of the version 2 of the GNU General Public License
8e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * as published by the Free Software Foundation
9e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *
10e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * This program is distributed in the hope that it will be useful,
11e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * but WITHOUT ANY WARRANTY; without even the implied warranty of
12e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * GNU General Public License for more details.
14e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger */
15e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
16e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger/*
17e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * If platform data are used you should have similar definitions
18e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * in your board-specific code:
19e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *
20e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *   static struct cc770_platform_data myboard_cc770_pdata = {
21e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           .osc_freq = 16000000,
22e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           .cir = 0x41,
23e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           .cor = 0x20,
24e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           .bcr = 0x40,
25e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *   };
26e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *
27e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * Please see include/linux/can/platform/cc770.h for description of
28e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * above fields.
29e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *
30e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * If the device tree is used, you need a CAN node definition in your
31e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * DTS file similar to:
32e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *
33e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *   can@3,100 {
34e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           compatible = "bosch,cc770";
35e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           reg = <3 0x100 0x80>;
36e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           interrupts = <2 0>;
37e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           interrupt-parent = <&mpic>;
38e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *           bosch,external-clock-frequency = <16000000>;
39e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *   };
40e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger *
41e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * See "Documentation/devicetree/bindings/net/can/cc770.txt" for further
42e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger * information.
43e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger */
44e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
45e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/kernel.h>
46e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/module.h>
47e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/interrupt.h>
48e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/netdevice.h>
49e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/delay.h>
50e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/platform_device.h>
51e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/of.h>
52e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/can.h>
53e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/can/dev.h>
54e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include <linux/can/platform/cc770.h>
55e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
56e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#include "cc770.h"
57e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
58e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#define DRV_NAME "cc770_platform"
59e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
60e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang GrandeggerMODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
61e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang GrandeggerMODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus");
62e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang GrandeggerMODULE_LICENSE("GPL v2");
63e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
64e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger#define CC770_PLATFORM_CAN_CLOCK  16000000
65e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
66e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerstatic u8 cc770_platform_read_reg(const struct cc770_priv *priv, int reg)
67e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger{
68e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	return ioread8(priv->reg_base + reg);
69e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger}
70e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
71e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerstatic void cc770_platform_write_reg(const struct cc770_priv *priv, int reg,
72e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger				     u8 val)
73e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger{
74e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	iowrite8(val, priv->reg_base + reg);
75e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger}
76e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
77e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerstatic int __devinit cc770_get_of_node_data(struct platform_device *pdev,
78e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger					    struct cc770_priv *priv)
79e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger{
80e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	struct device_node *np = pdev->dev.of_node;
81e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	const u32 *prop;
82e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	int prop_size;
83e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	u32 clkext;
84e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
85e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	prop = of_get_property(np, "bosch,external-clock-frequency",
86e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			       &prop_size);
87e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (prop && (prop_size ==  sizeof(u32)))
88e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		clkext = *prop;
89e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	else
90e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		clkext = CC770_PLATFORM_CAN_CLOCK; /* default */
91e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->can.clock.freq = clkext;
92e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
93e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	/* The system clock may not exceed 10 MHz */
94e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (priv->can.clock.freq > 10000000) {
95e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->cpu_interface |= CPUIF_DSC;
96e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->can.clock.freq /= 2;
97e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	}
98e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
99e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	/* The memory clock may not exceed 8 MHz */
100e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (priv->can.clock.freq > 8000000)
101e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->cpu_interface |= CPUIF_DMC;
102e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
103e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (of_get_property(np, "bosch,divide-memory-clock", NULL))
104e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->cpu_interface |= CPUIF_DMC;
105e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (of_get_property(np, "bosch,iso-low-speed-mux", NULL))
106e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->cpu_interface |= CPUIF_MUX;
107e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
108e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (!of_get_property(np, "bosch,no-comperator-bypass", NULL))
109e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->bus_config |= BUSCFG_CBY;
110e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (of_get_property(np, "bosch,disconnect-rx0-input", NULL))
111e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->bus_config |= BUSCFG_DR0;
112e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (of_get_property(np, "bosch,disconnect-rx1-input", NULL))
113e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->bus_config |= BUSCFG_DR1;
114e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (of_get_property(np, "bosch,disconnect-tx1-output", NULL))
115e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->bus_config |= BUSCFG_DT1;
116e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (of_get_property(np, "bosch,polarity-dominant", NULL))
117e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->bus_config |= BUSCFG_POL;
118e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
119e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	prop = of_get_property(np, "bosch,clock-out-frequency", &prop_size);
120e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (prop && (prop_size == sizeof(u32)) && *prop > 0) {
121e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		u32 cdv = clkext / *prop;
122e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		int slew;
123e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
124e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		if (cdv > 0 && cdv < 16) {
125e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			priv->cpu_interface |= CPUIF_CEN;
126e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			priv->clkout |= (cdv - 1) & CLKOUT_CD_MASK;
127e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
128e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			prop = of_get_property(np, "bosch,slew-rate",
129e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger					       &prop_size);
130e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			if (prop && (prop_size == sizeof(u32))) {
131e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger				slew = *prop;
132e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			} else {
133e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger				/* Determine default slew rate */
134e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger				slew = (CLKOUT_SL_MASK >>
135e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger					CLKOUT_SL_SHIFT) -
136e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger					((cdv * clkext - 1) / 8000000);
137e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger				if (slew < 0)
138e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger					slew = 0;
139e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			}
140e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			priv->clkout |= (slew << CLKOUT_SL_SHIFT) &
141e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger				CLKOUT_SL_MASK;
142e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		} else {
143e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			dev_dbg(&pdev->dev, "invalid clock-out-frequency\n");
144e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		}
145e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	}
146e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
147e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	return 0;
148e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger}
149e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
150e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerstatic int __devinit cc770_get_platform_data(struct platform_device *pdev,
151e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger					     struct cc770_priv *priv)
152e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger{
153e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
154e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	struct cc770_platform_data *pdata = pdev->dev.platform_data;
155e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
156e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->can.clock.freq = pdata->osc_freq;
157e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (priv->cpu_interface | CPUIF_DSC)
158e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		priv->can.clock.freq /= 2;
159e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->clkout = pdata->cor;
160e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->bus_config = pdata->bcr;
161e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->cpu_interface = pdata->cir;
162e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
163e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	return 0;
164e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger}
165e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
166e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerstatic int __devinit cc770_platform_probe(struct platform_device *pdev)
167e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger{
168e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	struct net_device *dev;
169e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	struct cc770_priv *priv;
170e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	struct resource *mem;
171e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	resource_size_t mem_size;
172e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	void __iomem *base;
173e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	int err, irq;
174e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
175e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
176e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	irq = platform_get_irq(pdev, 0);
177e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (!mem || irq <= 0)
178e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		return -ENODEV;
179e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
180e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	mem_size = resource_size(mem);
181e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (!request_mem_region(mem->start, mem_size, pdev->name))
182e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		return -EBUSY;
183e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
184e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	base = ioremap(mem->start, mem_size);
185e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (!base) {
186e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		err = -ENOMEM;
187e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		goto exit_release_mem;
188e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	}
189e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
190e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	dev = alloc_cc770dev(0);
191e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (!dev) {
192e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		err = -ENOMEM;
193e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		goto exit_unmap_mem;
194e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	}
195e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
196e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	dev->irq = irq;
197e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv = netdev_priv(dev);
198e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->read_reg = cc770_platform_read_reg;
199e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->write_reg = cc770_platform_write_reg;
200e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->irq_flags = IRQF_SHARED;
201e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	priv->reg_base = base;
202e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
203e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (pdev->dev.of_node)
204e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		err = cc770_get_of_node_data(pdev, priv);
205e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	else if (pdev->dev.platform_data)
206e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		err = cc770_get_platform_data(pdev, priv);
207e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	else
208e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		err = -ENODEV;
209e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (err)
210e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		goto exit_free_cc770;
211e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
212e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	dev_dbg(&pdev->dev,
213e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		 "reg_base=0x%p irq=%d clock=%d cpu_interface=0x%02x "
214e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		 "bus_config=0x%02x clkout=0x%02x\n",
215e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		 priv->reg_base, dev->irq, priv->can.clock.freq,
216e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		 priv->cpu_interface, priv->bus_config, priv->clkout);
217e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
218e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	dev_set_drvdata(&pdev->dev, dev);
219e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	SET_NETDEV_DEV(dev, &pdev->dev);
220e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
221e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	err = register_cc770dev(dev);
222e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	if (err) {
223e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		dev_err(&pdev->dev,
224e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger			"couldn't register CC700 device (err=%d)\n", err);
225e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		goto exit_free_cc770;
226e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	}
227e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
228e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	return 0;
229e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
230e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerexit_free_cc770:
231e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	free_cc770dev(dev);
232e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerexit_unmap_mem:
233e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	iounmap(base);
234e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerexit_release_mem:
235e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	release_mem_region(mem->start, mem_size);
236e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
237e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	return err;
238e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger}
239e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
240e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerstatic int __devexit cc770_platform_remove(struct platform_device *pdev)
241e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger{
242e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	struct net_device *dev = dev_get_drvdata(&pdev->dev);
243e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	struct cc770_priv *priv = netdev_priv(dev);
244e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	struct resource *mem;
245e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
246e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	unregister_cc770dev(dev);
247e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	iounmap(priv->reg_base);
248e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	free_cc770dev(dev);
249e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
250e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
251e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	release_mem_region(mem->start, resource_size(mem));
252e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
253e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	return 0;
254e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger}
255e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
256e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerstatic struct of_device_id __devinitdata cc770_platform_table[] = {
257e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	{.compatible = "bosch,cc770"}, /* CC770 from Bosch */
258e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	{.compatible = "intc,82527"},  /* AN82527 from Intel CP */
259e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	{},
260e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger};
261e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
262e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggerstatic struct platform_driver cc770_platform_driver = {
263e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	.driver = {
264e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		.name = DRV_NAME,
265e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		.owner = THIS_MODULE,
266e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger		.of_match_table = cc770_platform_table,
267e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	},
268e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	.probe = cc770_platform_probe,
269e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger	.remove = __devexit_p(cc770_platform_remove),
270e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger};
271e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandegger
272e285e44d91fe5a89e0d9fe4f5dda4f9e8c8a3c7eWolfgang Grandeggermodule_platform_driver(cc770_platform_driver);
273