mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 02:19:51 +00:00
![Uwe Kleine-König](/assets/img/avatar_default.png)
The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Link: https://lore.kernel.org/r/20230512212725.143824-15-u.kleine-koenig@pengutronix.de Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
312 lines
7.7 KiB
C
312 lines
7.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/io.h>
|
|
#include <linux/can/dev.h>
|
|
#include <linux/can/platform/sja1000.h>
|
|
|
|
#include "sja1000.h"
|
|
|
|
#define DRV_NAME "sja1000_isa"
|
|
|
|
#define MAXDEV 8
|
|
|
|
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
|
|
MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the ISA bus");
|
|
MODULE_LICENSE("GPL v2");
|
|
|
|
#define CLK_DEFAULT 16000000 /* 16 MHz */
|
|
#define CDR_DEFAULT (CDR_CBP | CDR_CLK_OFF)
|
|
#define OCR_DEFAULT OCR_TX0_PUSHPULL
|
|
|
|
static unsigned long port[MAXDEV];
|
|
static unsigned long mem[MAXDEV];
|
|
static int irq[MAXDEV];
|
|
static int clk[MAXDEV];
|
|
static unsigned char cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
|
static unsigned char ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
|
|
static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
|
|
static spinlock_t indirect_lock[MAXDEV]; /* lock for indirect access mode */
|
|
|
|
module_param_hw_array(port, ulong, ioport, NULL, 0444);
|
|
MODULE_PARM_DESC(port, "I/O port number");
|
|
|
|
module_param_hw_array(mem, ulong, iomem, NULL, 0444);
|
|
MODULE_PARM_DESC(mem, "I/O memory address");
|
|
|
|
module_param_hw_array(indirect, int, ioport, NULL, 0444);
|
|
MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
|
|
|
|
module_param_hw_array(irq, int, irq, NULL, 0444);
|
|
MODULE_PARM_DESC(irq, "IRQ number");
|
|
|
|
module_param_array(clk, int, NULL, 0444);
|
|
MODULE_PARM_DESC(clk, "External oscillator clock frequency "
|
|
"(default=16000000 [16 MHz])");
|
|
|
|
module_param_array(cdr, byte, NULL, 0444);
|
|
MODULE_PARM_DESC(cdr, "Clock divider register "
|
|
"(default=0x48 [CDR_CBP | CDR_CLK_OFF])");
|
|
|
|
module_param_array(ocr, byte, NULL, 0444);
|
|
MODULE_PARM_DESC(ocr, "Output control register "
|
|
"(default=0x18 [OCR_TX0_PUSHPULL])");
|
|
|
|
#define SJA1000_IOSIZE 0x20
|
|
#define SJA1000_IOSIZE_INDIRECT 0x02
|
|
|
|
static struct platform_device *sja1000_isa_devs[MAXDEV];
|
|
|
|
static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg)
|
|
{
|
|
return readb(priv->reg_base + reg);
|
|
}
|
|
|
|
static void sja1000_isa_mem_write_reg(const struct sja1000_priv *priv,
|
|
int reg, u8 val)
|
|
{
|
|
writeb(val, priv->reg_base + reg);
|
|
}
|
|
|
|
static u8 sja1000_isa_port_read_reg(const struct sja1000_priv *priv, int reg)
|
|
{
|
|
return inb((unsigned long)priv->reg_base + reg);
|
|
}
|
|
|
|
static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv,
|
|
int reg, u8 val)
|
|
{
|
|
outb(val, (unsigned long)priv->reg_base + reg);
|
|
}
|
|
|
|
static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv,
|
|
int reg)
|
|
{
|
|
unsigned long flags, base = (unsigned long)priv->reg_base;
|
|
u8 readval;
|
|
|
|
spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags);
|
|
outb(reg, base);
|
|
readval = inb(base + 1);
|
|
spin_unlock_irqrestore(&indirect_lock[priv->dev->dev_id], flags);
|
|
|
|
return readval;
|
|
}
|
|
|
|
static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv,
|
|
int reg, u8 val)
|
|
{
|
|
unsigned long flags, base = (unsigned long)priv->reg_base;
|
|
|
|
spin_lock_irqsave(&indirect_lock[priv->dev->dev_id], flags);
|
|
outb(reg, base);
|
|
outb(val, base + 1);
|
|
spin_unlock_irqrestore(&indirect_lock[priv->dev->dev_id], flags);
|
|
}
|
|
|
|
static int sja1000_isa_probe(struct platform_device *pdev)
|
|
{
|
|
struct net_device *dev;
|
|
struct sja1000_priv *priv;
|
|
void __iomem *base = NULL;
|
|
int iosize = SJA1000_IOSIZE;
|
|
int idx = pdev->id;
|
|
int err;
|
|
|
|
dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
|
|
idx, port[idx], mem[idx], irq[idx]);
|
|
|
|
if (mem[idx]) {
|
|
if (!request_mem_region(mem[idx], iosize, DRV_NAME)) {
|
|
err = -EBUSY;
|
|
goto exit;
|
|
}
|
|
base = ioremap(mem[idx], iosize);
|
|
if (!base) {
|
|
err = -ENOMEM;
|
|
goto exit_release;
|
|
}
|
|
} else {
|
|
if (indirect[idx] > 0 ||
|
|
(indirect[idx] == -1 && indirect[0] > 0))
|
|
iosize = SJA1000_IOSIZE_INDIRECT;
|
|
if (!request_region(port[idx], iosize, DRV_NAME)) {
|
|
err = -EBUSY;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
dev = alloc_sja1000dev(0);
|
|
if (!dev) {
|
|
err = -ENOMEM;
|
|
goto exit_unmap;
|
|
}
|
|
priv = netdev_priv(dev);
|
|
|
|
dev->irq = irq[idx];
|
|
priv->irq_flags = IRQF_SHARED;
|
|
if (mem[idx]) {
|
|
priv->reg_base = base;
|
|
dev->base_addr = mem[idx];
|
|
priv->read_reg = sja1000_isa_mem_read_reg;
|
|
priv->write_reg = sja1000_isa_mem_write_reg;
|
|
} else {
|
|
priv->reg_base = (void __iomem *)port[idx];
|
|
dev->base_addr = port[idx];
|
|
|
|
if (iosize == SJA1000_IOSIZE_INDIRECT) {
|
|
priv->read_reg = sja1000_isa_port_read_reg_indirect;
|
|
priv->write_reg = sja1000_isa_port_write_reg_indirect;
|
|
spin_lock_init(&indirect_lock[idx]);
|
|
} else {
|
|
priv->read_reg = sja1000_isa_port_read_reg;
|
|
priv->write_reg = sja1000_isa_port_write_reg;
|
|
}
|
|
}
|
|
|
|
if (clk[idx])
|
|
priv->can.clock.freq = clk[idx] / 2;
|
|
else if (clk[0])
|
|
priv->can.clock.freq = clk[0] / 2;
|
|
else
|
|
priv->can.clock.freq = CLK_DEFAULT / 2;
|
|
|
|
if (ocr[idx] != 0xff)
|
|
priv->ocr = ocr[idx];
|
|
else if (ocr[0] != 0xff)
|
|
priv->ocr = ocr[0];
|
|
else
|
|
priv->ocr = OCR_DEFAULT;
|
|
|
|
if (cdr[idx] != 0xff)
|
|
priv->cdr = cdr[idx];
|
|
else if (cdr[0] != 0xff)
|
|
priv->cdr = cdr[0];
|
|
else
|
|
priv->cdr = CDR_DEFAULT;
|
|
|
|
platform_set_drvdata(pdev, dev);
|
|
SET_NETDEV_DEV(dev, &pdev->dev);
|
|
dev->dev_id = idx;
|
|
|
|
err = register_sja1000dev(dev);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
|
|
DRV_NAME, err);
|
|
goto exit_free;
|
|
}
|
|
|
|
dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n",
|
|
DRV_NAME, priv->reg_base, dev->irq);
|
|
return 0;
|
|
|
|
exit_free:
|
|
free_sja1000dev(dev);
|
|
exit_unmap:
|
|
if (mem[idx])
|
|
iounmap(base);
|
|
exit_release:
|
|
if (mem[idx])
|
|
release_mem_region(mem[idx], iosize);
|
|
else
|
|
release_region(port[idx], iosize);
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
static void sja1000_isa_remove(struct platform_device *pdev)
|
|
{
|
|
struct net_device *dev = platform_get_drvdata(pdev);
|
|
struct sja1000_priv *priv = netdev_priv(dev);
|
|
int idx = pdev->id;
|
|
|
|
unregister_sja1000dev(dev);
|
|
|
|
if (mem[idx]) {
|
|
iounmap(priv->reg_base);
|
|
release_mem_region(mem[idx], SJA1000_IOSIZE);
|
|
} else {
|
|
if (priv->read_reg == sja1000_isa_port_read_reg_indirect)
|
|
release_region(port[idx], SJA1000_IOSIZE_INDIRECT);
|
|
else
|
|
release_region(port[idx], SJA1000_IOSIZE);
|
|
}
|
|
free_sja1000dev(dev);
|
|
}
|
|
|
|
static struct platform_driver sja1000_isa_driver = {
|
|
.probe = sja1000_isa_probe,
|
|
.remove_new = sja1000_isa_remove,
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
},
|
|
};
|
|
|
|
static int __init sja1000_isa_init(void)
|
|
{
|
|
int idx, err;
|
|
|
|
for (idx = 0; idx < MAXDEV; idx++) {
|
|
if ((port[idx] || mem[idx]) && irq[idx]) {
|
|
sja1000_isa_devs[idx] =
|
|
platform_device_alloc(DRV_NAME, idx);
|
|
if (!sja1000_isa_devs[idx]) {
|
|
err = -ENOMEM;
|
|
goto exit_free_devices;
|
|
}
|
|
err = platform_device_add(sja1000_isa_devs[idx]);
|
|
if (err) {
|
|
platform_device_put(sja1000_isa_devs[idx]);
|
|
goto exit_free_devices;
|
|
}
|
|
pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, "
|
|
"irq=%d\n",
|
|
DRV_NAME, idx, port[idx], mem[idx], irq[idx]);
|
|
} else if (idx == 0 || port[idx] || mem[idx]) {
|
|
pr_err("%s: insufficient parameters supplied\n",
|
|
DRV_NAME);
|
|
err = -EINVAL;
|
|
goto exit_free_devices;
|
|
}
|
|
}
|
|
|
|
err = platform_driver_register(&sja1000_isa_driver);
|
|
if (err)
|
|
goto exit_free_devices;
|
|
|
|
pr_info("Legacy %s driver for max. %d devices registered\n",
|
|
DRV_NAME, MAXDEV);
|
|
|
|
return 0;
|
|
|
|
exit_free_devices:
|
|
while (--idx >= 0) {
|
|
if (sja1000_isa_devs[idx])
|
|
platform_device_unregister(sja1000_isa_devs[idx]);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void __exit sja1000_isa_exit(void)
|
|
{
|
|
int idx;
|
|
|
|
platform_driver_unregister(&sja1000_isa_driver);
|
|
for (idx = 0; idx < MAXDEV; idx++) {
|
|
if (sja1000_isa_devs[idx])
|
|
platform_device_unregister(sja1000_isa_devs[idx]);
|
|
}
|
|
}
|
|
|
|
module_init(sja1000_isa_init);
|
|
module_exit(sja1000_isa_exit);
|