drivers/net/can/spi/mcp251x.c

Source file repositories/reference/linux-study-clean/drivers/net/can/spi/mcp251x.c

File Facts

System
Linux kernel
Corpus path
drivers/net/can/spi/mcp251x.c
Extension
.c
Size
40859 bytes
Lines
1573
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct net_device_ops mcp251x_netdev_ops = {
	.ndo_open = mcp251x_open,
	.ndo_stop = mcp251x_stop,
	.ndo_start_xmit = mcp251x_hard_start_xmit,
};

static const struct ethtool_ops mcp251x_ethtool_ops = {
	.get_ts_info = ethtool_op_get_ts_info,
};

static const struct of_device_id mcp251x_of_match[] = {
	{
		.compatible	= "microchip,mcp2510",
		.data		= (void *)CAN_MCP251X_MCP2510,
	},
	{
		.compatible	= "microchip,mcp2515",
		.data		= (void *)CAN_MCP251X_MCP2515,
	},
	{
		.compatible	= "microchip,mcp25625",
		.data		= (void *)CAN_MCP251X_MCP25625,
	},
	{ }
};
MODULE_DEVICE_TABLE(of, mcp251x_of_match);

static const struct spi_device_id mcp251x_id_table[] = {
	{
		.name		= "mcp2510",
		.driver_data	= (kernel_ulong_t)CAN_MCP251X_MCP2510,
	},
	{
		.name		= "mcp2515",
		.driver_data	= (kernel_ulong_t)CAN_MCP251X_MCP2515,
	},
	{
		.name		= "mcp25625",
		.driver_data	= (kernel_ulong_t)CAN_MCP251X_MCP25625,
	},
	{ }
};
MODULE_DEVICE_TABLE(spi, mcp251x_id_table);

static int mcp251x_can_probe(struct spi_device *spi)
{
	struct net_device *net;
	struct mcp251x_priv *priv;
	struct clk *clk;
	u32 freq;
	int ret;

	clk = devm_clk_get_optional(&spi->dev, NULL);
	if (IS_ERR(clk))
		return dev_err_probe(&spi->dev, PTR_ERR(clk), "Cannot get clock\n");

	freq = clk_get_rate(clk);
	if (freq == 0)
		device_property_read_u32(&spi->dev, "clock-frequency", &freq);

	/* Sanity check */
	if (freq < 1000000 || freq > 25000000)
		return dev_err_probe(&spi->dev, -ERANGE, "clock frequency out of range\n");

	/* Allocate can/net device */
	net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX);
	if (!net)
		return -ENOMEM;

	ret = clk_prepare_enable(clk);
	if (ret) {
		dev_err_probe(&spi->dev, ret, "Cannot enable clock\n");
		goto out_free;
	}

	net->netdev_ops = &mcp251x_netdev_ops;
	net->ethtool_ops = &mcp251x_ethtool_ops;
	net->flags |= IFF_ECHO;

	priv = netdev_priv(net);
	priv->can.bittiming_const = &mcp251x_bittiming_const;
	priv->can.do_set_mode = mcp251x_do_set_mode;
	priv->can.clock.freq = freq / 2;
	priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
		CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY;
	priv->model = (enum mcp251x_model)(uintptr_t)spi_get_device_match_data(spi);
	priv->net = net;
	priv->clk = clk;

	spi_set_drvdata(spi, priv);

Annotation

Implementation Notes