Bug fixes for main IPMI driver, kcs updates

A couple of bug fixes for the main IPMI driver, one functional and two
 annotations.
 
 The kcs driver has some significant updates that have been pending for a
 while, but I forgot to include in next until a week ago.  But this code
 is only used by the people who are sending it to me, really, so it's not
 a big deal.  I did want it to sit in next for at least a week, and it did
 result in a fix.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAl6Qc3AACgkQYfOMkJGb
 /4EOxhAAtEkoudrXYDDd39B+wVjW0JZ59jBKtTiosLrK+mE0hyq5rh+lJLqmtKmy
 uF69HLqtSGl7ZgpFdzeT8M1fRt4uTTh7BuOkkc2r/18/a+YVJOUFnUpB4BLg06O8
 Wr1dMeya6KukM+S3clezCx7jwkwu3V+E8lUKxkQg6zIHRw+DevNUL2Ec6MpZI4SJ
 KIjmyp62ASIuME0YeEXnx3wrFzOJIR+01liu4R/4OcDBR2p6fb/TqPFwY4HCq9Fy
 pV7riuqXLTGKYk9vnmeHMYZGXT8I55FvkQIB73QWRSpcIyLLcht0wQ04dRQFF/jA
 j4ZLfGNTvHgnJQiLmRjPIGGbmiUPx76fWghAC87QtT07HksYEvnsbbxuZ/lZTGdc
 NmV7X25HHr/YM4wEffALiaOHXzWXkuxkN0ptzrulbyYOE3631jwAMhcyzucvPvTO
 7wU5qshQHTLqQLwyvChdThsKNukmaqOdBH9/vONsXFvgcLy14ZNyR3oPObQUpD6n
 CtKbteXSuIA2cjYBj1oCgZ/3lwmYd/DVqGhqGYCnNElnSekF14p312lE+NiXYxnZ
 7BIqKBn51asdeWdeJNFOk0qZog6YquwCM7m16kSukzehPaUsovuJkDpVGKpEhYEg
 2M4aiiDr/gWpoXJBg6xMM2j//268z6Oe3C6ZX1XPD1jzZ8Gv/Y0=
 =vV/F
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-5.7-1' of git://github.com/cminyard/linux-ipmi

Pull IPMI updates from Corey Minyard:
 "Bug fixes for main IPMI driver, kcs updates

  A couple of bug fixes for the main IPMI driver, one functional and two
  annotations.

  The kcs driver has some significant updates that have been pending for
  a while, but I forgot to include in next until a week ago. But this
  code is only used by the people who are sending it to me, really, so
  it's not a big deal. I did want it to sit in next for at least a week,
  and it did result in a fix"

* tag 'for-linus-5.7-1' of git://github.com/cminyard/linux-ipmi:
  ipmi: kcs: Fix aspeed_kcs_probe_of_v1()
  ipmi: Add missing annotation for ipmi_ssif_lock_cond() and ipmi_ssif_unlock_cond()
  ipmi: kcs: aspeed: Implement v2 bindings
  ipmi: kcs: Finish configuring ASPEED KCS device before enable
  dt-bindings: ipmi: aspeed: Introduce a v2 binding for KCS
  ipmi: fix hung processes in __get_guid()
  drivers: char: ipmi: ipmi_msghandler: Pass lockdep expression to RCU lists
This commit is contained in:
Linus Torvalds 2020-04-10 12:43:42 -07:00
commit 6900433e0f
4 changed files with 158 additions and 43 deletions

View File

@ -1,9 +1,10 @@
* Aspeed KCS (Keyboard Controller Style) IPMI interface
# Aspeed KCS (Keyboard Controller Style) IPMI interface
The Aspeed SOCs (AST2400 and AST2500) are commonly used as BMCs
(Baseboard Management Controllers) and the KCS interface can be
used to perform in-band IPMI communication with their host.
## v1
Required properties:
- compatible : should be one of
"aspeed,ast2400-kcs-bmc"
@ -12,14 +13,21 @@ Required properties:
- kcs_chan : The LPC channel number in the controller
- kcs_addr : The host CPU IO map address
## v2
Required properties:
- compatible : should be one of
"aspeed,ast2400-kcs-bmc-v2"
"aspeed,ast2500-kcs-bmc-v2"
- reg : The address and size of the IDR, ODR and STR registers
- interrupts : interrupt generated by the controller
- aspeed,lpc-io-reg : The host CPU LPC IO address for the device
Example:
kcs3: kcs3@0 {
compatible = "aspeed,ast2500-kcs-bmc";
reg = <0x0 0x80>;
kcs3: kcs@24 {
compatible = "aspeed,ast2500-kcs-bmc-v2";
reg = <0x24 0x1>, <0x30 0x1>, <0x3c 0x1>;
aspeed,lpc-reg = <0xca2>;
interrupts = <8>;
kcs_chan = <3>;
kcs_addr = <0xCA2>;
status = "okay";
};

View File

@ -618,6 +618,8 @@ static DEFINE_MUTEX(ipmidriver_mutex);
static LIST_HEAD(ipmi_interfaces);
static DEFINE_MUTEX(ipmi_interfaces_mutex);
#define ipmi_interfaces_mutex_held() \
lockdep_is_held(&ipmi_interfaces_mutex)
static struct srcu_struct ipmi_interfaces_srcu;
/*
@ -1321,7 +1323,8 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
* synchronize_srcu()) then free everything in that list.
*/
mutex_lock(&intf->cmd_rcvrs_mutex);
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link,
lockdep_is_held(&intf->cmd_rcvrs_mutex)) {
if (rcvr->user == user) {
list_del_rcu(&rcvr->link);
rcvr->next = rcvrs;
@ -1599,7 +1602,8 @@ static struct cmd_rcvr *find_cmd_rcvr(struct ipmi_smi *intf,
{
struct cmd_rcvr *rcvr;
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link,
lockdep_is_held(&intf->cmd_rcvrs_mutex)) {
if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
&& (rcvr->chans & (1 << chan)))
return rcvr;
@ -1614,7 +1618,8 @@ static int is_cmd_rcvr_exclusive(struct ipmi_smi *intf,
{
struct cmd_rcvr *rcvr;
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link,
lockdep_is_held(&intf->cmd_rcvrs_mutex)) {
if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
&& (rcvr->chans & chans))
return 0;
@ -3188,8 +3193,8 @@ static void __get_guid(struct ipmi_smi *intf)
if (rv)
/* Send failed, no GUID available. */
bmc->dyn_guid_set = 0;
wait_event(intf->waitq, bmc->dyn_guid_set != 2);
else
wait_event(intf->waitq, bmc->dyn_guid_set != 2);
/* dyn_guid_set makes the guid data available. */
smp_rmb();
@ -3450,7 +3455,8 @@ int ipmi_add_smi(struct module *owner,
/* Look for a hole in the numbers. */
i = 0;
link = &ipmi_interfaces;
list_for_each_entry_rcu(tintf, &ipmi_interfaces, link) {
list_for_each_entry_rcu(tintf, &ipmi_interfaces, link,
ipmi_interfaces_mutex_held()) {
if (tintf->intf_num != i) {
link = &tintf->link;
break;

View File

@ -313,6 +313,7 @@ static int start_send(struct ssif_info *ssif_info,
static unsigned long *ipmi_ssif_lock_cond(struct ssif_info *ssif_info,
unsigned long *flags)
__acquires(&ssif_info->lock)
{
spin_lock_irqsave(&ssif_info->lock, *flags);
return flags;
@ -320,6 +321,7 @@ static unsigned long *ipmi_ssif_lock_cond(struct ssif_info *ssif_info,
static void ipmi_ssif_unlock_cond(struct ssif_info *ssif_info,
unsigned long *flags)
__releases(&ssif_info->lock)
{
spin_unlock_irqrestore(&ssif_info->lock, *flags);
}

View File

@ -12,6 +12,7 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/regmap.h>
@ -233,58 +234,154 @@ static const struct kcs_ioreg ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = {
{ .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 },
};
static struct kcs_bmc *aspeed_kcs_probe_of_v1(struct platform_device *pdev)
{
struct aspeed_kcs_bmc *priv;
struct device_node *np;
struct kcs_bmc *kcs;
u32 channel;
u32 slave;
int rc;
np = pdev->dev.of_node;
rc = of_property_read_u32(np, "kcs_chan", &channel);
if ((rc != 0) || (channel == 0 || channel > KCS_CHANNEL_MAX)) {
dev_err(&pdev->dev, "no valid 'kcs_chan' configured\n");
return ERR_PTR(-EINVAL);
}
kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
if (!kcs)
return ERR_PTR(-ENOMEM);
priv = kcs_bmc_priv(kcs);
priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
if (IS_ERR(priv->map)) {
dev_err(&pdev->dev, "Couldn't get regmap\n");
return ERR_PTR(-ENODEV);
}
rc = of_property_read_u32(np, "kcs_addr", &slave);
if (rc) {
dev_err(&pdev->dev, "no valid 'kcs_addr' configured\n");
return ERR_PTR(-EINVAL);
}
kcs->ioreg = ast_kcs_bmc_ioregs[channel - 1];
aspeed_kcs_set_address(kcs, slave);
return kcs;
}
static int aspeed_kcs_calculate_channel(const struct kcs_ioreg *regs)
{
int i;
for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
if (!memcmp(&ast_kcs_bmc_ioregs[i], regs, sizeof(*regs)))
return i + 1;
}
return -EINVAL;
}
static struct kcs_bmc *aspeed_kcs_probe_of_v2(struct platform_device *pdev)
{
struct aspeed_kcs_bmc *priv;
struct device_node *np;
struct kcs_ioreg ioreg;
struct kcs_bmc *kcs;
const __be32 *reg;
int channel;
u32 slave;
int rc;
np = pdev->dev.of_node;
/* Don't translate addresses, we want offsets for the regmaps */
reg = of_get_address(np, 0, NULL, NULL);
if (!reg)
return ERR_PTR(-EINVAL);
ioreg.idr = be32_to_cpup(reg);
reg = of_get_address(np, 1, NULL, NULL);
if (!reg)
return ERR_PTR(-EINVAL);
ioreg.odr = be32_to_cpup(reg);
reg = of_get_address(np, 2, NULL, NULL);
if (!reg)
return ERR_PTR(-EINVAL);
ioreg.str = be32_to_cpup(reg);
channel = aspeed_kcs_calculate_channel(&ioreg);
if (channel < 0)
return ERR_PTR(channel);
kcs = kcs_bmc_alloc(&pdev->dev, sizeof(struct aspeed_kcs_bmc), channel);
if (!kcs)
return ERR_PTR(-ENOMEM);
kcs->ioreg = ioreg;
priv = kcs_bmc_priv(kcs);
priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
if (IS_ERR(priv->map)) {
dev_err(&pdev->dev, "Couldn't get regmap\n");
return ERR_PTR(-ENODEV);
}
rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &slave);
if (rc)
return ERR_PTR(rc);
aspeed_kcs_set_address(kcs, slave);
return kcs;
}
static int aspeed_kcs_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct aspeed_kcs_bmc *priv;
struct kcs_bmc *kcs_bmc;
u32 chan, addr;
struct device_node *np;
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;
}
np = pdev->dev.of_node;
if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc") ||
of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc"))
kcs_bmc = aspeed_kcs_probe_of_v1(pdev);
else if (of_device_is_compatible(np, "aspeed,ast2400-kcs-bmc-v2") ||
of_device_is_compatible(np, "aspeed,ast2500-kcs-bmc-v2"))
kcs_bmc = aspeed_kcs_probe_of_v2(pdev);
else
return -EINVAL;
rc = of_property_read_u32(dev->of_node, "kcs_addr", &addr);
if (rc) {
dev_err(dev, "no valid 'kcs_addr' configured\n");
return -ENODEV;
}
if (IS_ERR(kcs_bmc))
return PTR_ERR(kcs_bmc);
kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan);
if (!kcs_bmc)
return -ENOMEM;
priv = kcs_bmc_priv(kcs_bmc);
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;
}
kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1];
kcs_bmc->io_inputb = aspeed_kcs_inb;
kcs_bmc->io_outputb = aspeed_kcs_outb;
dev_set_drvdata(dev, kcs_bmc);
aspeed_kcs_set_address(kcs_bmc, addr);
aspeed_kcs_enable_channel(kcs_bmc, true);
rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
if (rc)
return rc;
dev_set_drvdata(dev, kcs_bmc);
aspeed_kcs_enable_channel(kcs_bmc, true);
rc = misc_register(&kcs_bmc->miscdev);
if (rc) {
dev_err(dev, "Unable to register device\n");
return rc;
}
pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n",
chan, addr,
kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
dev_dbg(&pdev->dev,
"Probed KCS device %d (IDR=0x%x, ODR=0x%x, STR=0x%x)\n",
kcs_bmc->channel, kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr,
kcs_bmc->ioreg.str);
return 0;
}
@ -301,6 +398,8 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
static const struct of_device_id ast_kcs_bmc_match[] = {
{ .compatible = "aspeed,ast2400-kcs-bmc" },
{ .compatible = "aspeed,ast2500-kcs-bmc" },
{ .compatible = "aspeed,ast2400-kcs-bmc-v2" },
{ .compatible = "aspeed,ast2500-kcs-bmc-v2" },
{ }
};
MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match);