999dff3c13
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() will be 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> Message-Id: <16144ffaa6f40a1a126d5cf19ef4337218a04fbb.1709655755.git.u.kleine-koenig@pengutronix.de> Signed-off-by: Corey Minyard <minyard@acm.org>
252 lines
6.5 KiB
C
252 lines
6.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2018, Nuvoton Corporation.
|
|
* Copyright (c) 2018, Intel Corporation.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "nuvoton-kcs-bmc: " fmt
|
|
|
|
#include <linux/atomic.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "kcs_bmc_device.h"
|
|
|
|
#define DEVICE_NAME "npcm-kcs-bmc"
|
|
#define KCS_CHANNEL_MAX 3
|
|
|
|
#define KCS1ST 0x0C
|
|
#define KCS2ST 0x1E
|
|
#define KCS3ST 0x30
|
|
|
|
#define KCS1DO 0x0E
|
|
#define KCS2DO 0x20
|
|
#define KCS3DO 0x32
|
|
|
|
#define KCS1DI 0x10
|
|
#define KCS2DI 0x22
|
|
#define KCS3DI 0x34
|
|
|
|
#define KCS1CTL 0x18
|
|
#define KCS2CTL 0x2A
|
|
#define KCS3CTL 0x3C
|
|
#define KCS_CTL_IBFIE BIT(0)
|
|
#define KCS_CTL_OBEIE BIT(1)
|
|
|
|
#define KCS1IE 0x1C
|
|
#define KCS2IE 0x2E
|
|
#define KCS3IE 0x40
|
|
#define KCS_IE_IRQE BIT(0)
|
|
#define KCS_IE_HIRQE BIT(3)
|
|
|
|
/*
|
|
* 7.2.4 Core KCS Registers
|
|
* Registers in this module are 8 bits. An 8-bit register must be accessed
|
|
* by an 8-bit read or write.
|
|
*
|
|
* sts: KCS Channel n Status Register (KCSnST).
|
|
* dob: KCS Channel n Data Out Buffer Register (KCSnDO).
|
|
* dib: KCS Channel n Data In Buffer Register (KCSnDI).
|
|
* ctl: KCS Channel n Control Register (KCSnCTL).
|
|
* ie : KCS Channel n Interrupt Enable Register (KCSnIE).
|
|
*/
|
|
struct npcm7xx_kcs_reg {
|
|
u32 sts;
|
|
u32 dob;
|
|
u32 dib;
|
|
u32 ctl;
|
|
u32 ie;
|
|
};
|
|
|
|
struct npcm7xx_kcs_bmc {
|
|
struct kcs_bmc_device kcs_bmc;
|
|
|
|
struct regmap *map;
|
|
|
|
const struct npcm7xx_kcs_reg *reg;
|
|
};
|
|
|
|
static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = {
|
|
{ .sts = KCS1ST, .dob = KCS1DO, .dib = KCS1DI, .ctl = KCS1CTL, .ie = KCS1IE },
|
|
{ .sts = KCS2ST, .dob = KCS2DO, .dib = KCS2DI, .ctl = KCS2CTL, .ie = KCS2IE },
|
|
{ .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL, .ie = KCS3IE },
|
|
};
|
|
|
|
static inline struct npcm7xx_kcs_bmc *to_npcm7xx_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
|
|
{
|
|
return container_of(kcs_bmc, struct npcm7xx_kcs_bmc, kcs_bmc);
|
|
}
|
|
|
|
static u8 npcm7xx_kcs_inb(struct kcs_bmc_device *kcs_bmc, u32 reg)
|
|
{
|
|
struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
|
|
u32 val = 0;
|
|
int rc;
|
|
|
|
rc = regmap_read(priv->map, reg, &val);
|
|
WARN(rc != 0, "regmap_read() failed: %d\n", rc);
|
|
|
|
return rc == 0 ? (u8)val : 0;
|
|
}
|
|
|
|
static void npcm7xx_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
|
|
{
|
|
struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
|
|
int rc;
|
|
|
|
rc = regmap_write(priv->map, reg, data);
|
|
WARN(rc != 0, "regmap_write() failed: %d\n", rc);
|
|
}
|
|
|
|
static void npcm7xx_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 data)
|
|
{
|
|
struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
|
|
int rc;
|
|
|
|
rc = regmap_update_bits(priv->map, reg, mask, data);
|
|
WARN(rc != 0, "regmap_update_bits() failed: %d\n", rc);
|
|
}
|
|
|
|
static void npcm7xx_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
|
|
{
|
|
struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
|
|
|
|
regmap_update_bits(priv->map, priv->reg->ie, KCS_IE_IRQE | KCS_IE_HIRQE,
|
|
enable ? KCS_IE_IRQE | KCS_IE_HIRQE : 0);
|
|
}
|
|
|
|
static void npcm7xx_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
|
|
{
|
|
struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
|
|
|
|
if (mask & KCS_BMC_EVENT_TYPE_OBE)
|
|
regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_OBEIE,
|
|
!!(state & KCS_BMC_EVENT_TYPE_OBE) * KCS_CTL_OBEIE);
|
|
|
|
if (mask & KCS_BMC_EVENT_TYPE_IBF)
|
|
regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
|
|
!!(state & KCS_BMC_EVENT_TYPE_IBF) * KCS_CTL_IBFIE);
|
|
}
|
|
|
|
static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
|
|
{
|
|
struct kcs_bmc_device *kcs_bmc = arg;
|
|
|
|
return kcs_bmc_handle_event(kcs_bmc);
|
|
}
|
|
|
|
static int npcm7xx_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
int irq;
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0)
|
|
return irq;
|
|
|
|
return devm_request_irq(dev, irq, npcm7xx_kcs_irq, IRQF_SHARED,
|
|
dev_name(dev), kcs_bmc);
|
|
}
|
|
|
|
static const struct kcs_bmc_device_ops npcm7xx_kcs_ops = {
|
|
.irq_mask_update = npcm7xx_kcs_irq_mask_update,
|
|
.io_inputb = npcm7xx_kcs_inb,
|
|
.io_outputb = npcm7xx_kcs_outb,
|
|
.io_updateb = npcm7xx_kcs_updateb,
|
|
};
|
|
|
|
static int npcm7xx_kcs_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct npcm7xx_kcs_bmc *priv;
|
|
struct kcs_bmc_device *kcs_bmc;
|
|
u32 chan;
|
|
int rc;
|
|
|
|
rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan);
|
|
if (rc != 0 || chan == 0 || chan > KCS_CHANNEL_MAX) {
|
|
dev_err(dev, "no valid 'kcs_chan' configured\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
priv->map = syscon_node_to_regmap(dev->parent->of_node);
|
|
if (IS_ERR(priv->map)) {
|
|
dev_err(dev, "Couldn't get regmap\n");
|
|
return -ENODEV;
|
|
}
|
|
priv->reg = &npcm7xx_kcs_reg_tbl[chan - 1];
|
|
|
|
kcs_bmc = &priv->kcs_bmc;
|
|
kcs_bmc->dev = &pdev->dev;
|
|
kcs_bmc->channel = chan;
|
|
kcs_bmc->ioreg.idr = priv->reg->dib;
|
|
kcs_bmc->ioreg.odr = priv->reg->dob;
|
|
kcs_bmc->ioreg.str = priv->reg->sts;
|
|
kcs_bmc->ops = &npcm7xx_kcs_ops;
|
|
|
|
platform_set_drvdata(pdev, priv);
|
|
|
|
rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
|
|
if (rc)
|
|
return rc;
|
|
|
|
npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
|
|
npcm7xx_kcs_enable_channel(kcs_bmc, true);
|
|
|
|
rc = kcs_bmc_add_device(kcs_bmc);
|
|
if (rc) {
|
|
dev_warn(&pdev->dev, "Failed to register channel %d: %d\n", kcs_bmc->channel, rc);
|
|
return rc;
|
|
}
|
|
|
|
pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
|
|
chan,
|
|
kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void npcm7xx_kcs_remove(struct platform_device *pdev)
|
|
{
|
|
struct npcm7xx_kcs_bmc *priv = platform_get_drvdata(pdev);
|
|
struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
|
|
|
|
kcs_bmc_remove_device(kcs_bmc);
|
|
|
|
npcm7xx_kcs_enable_channel(kcs_bmc, false);
|
|
npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
|
|
}
|
|
|
|
static const struct of_device_id npcm_kcs_bmc_match[] = {
|
|
{ .compatible = "nuvoton,npcm750-kcs-bmc" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, npcm_kcs_bmc_match);
|
|
|
|
static struct platform_driver npcm_kcs_bmc_driver = {
|
|
.driver = {
|
|
.name = DEVICE_NAME,
|
|
.of_match_table = npcm_kcs_bmc_match,
|
|
},
|
|
.probe = npcm7xx_kcs_probe,
|
|
.remove_new = npcm7xx_kcs_remove,
|
|
};
|
|
module_platform_driver(npcm_kcs_bmc_driver);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Avi Fishman <avifishman70@gmail.com>");
|
|
MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
|
|
MODULE_DESCRIPTION("NPCM7xx device interface to the KCS BMC device");
|