Skip to content

Commit 28651e6

Browse files
amboarcminyard
authored andcommitted
ipmi: kcs_bmc: Allow clients to control KCS IRQ state
Add a mechanism for controlling whether the client associated with a KCS device will receive Input Buffer Full (IBF) and Output Buffer Empty (OBE) events. This enables an abstract implementation of poll() for KCS devices. A wart in the implementation is that the ASPEED KCS devices don't support an OBE interrupt for the BMC. Instead we pretend it has one by polling the status register waiting for the Output Buffer Full (OBF) bit to clear, and generating an event when OBE is observed. Cc: CS20 KWLiu <[email protected]> Signed-off-by: Andrew Jeffery <[email protected]> Reviewed-by: Zev Weiss <[email protected]> Message-Id: <[email protected]> Signed-off-by: Corey Minyard <[email protected]>
1 parent 7cafff9 commit 28651e6

File tree

6 files changed

+131
-57
lines changed

6 files changed

+131
-57
lines changed

drivers/char/ipmi/kcs_bmc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,12 @@ void kcs_bmc_unregister_driver(struct kcs_bmc_driver *drv)
182182
}
183183
EXPORT_SYMBOL(kcs_bmc_unregister_driver);
184184

185+
void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events)
186+
{
187+
kcs_bmc->ops->irq_mask_update(kcs_bmc, mask, events);
188+
}
189+
EXPORT_SYMBOL(kcs_bmc_update_event_mask);
190+
185191
MODULE_LICENSE("GPL v2");
186192
MODULE_AUTHOR("Haiyue Wang <[email protected]>");
187193
MODULE_AUTHOR("Andrew Jeffery <[email protected]>");

drivers/char/ipmi/kcs_bmc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
#include <linux/list.h>
1010

11+
#define KCS_BMC_EVENT_TYPE_OBE BIT(0)
12+
#define KCS_BMC_EVENT_TYPE_IBF BIT(1)
13+
1114
#define KCS_BMC_STR_OBF BIT(0)
1215
#define KCS_BMC_STR_IBF BIT(1)
1316
#define KCS_BMC_STR_CMD_DAT BIT(3)

drivers/char/ipmi/kcs_bmc_aspeed.c

Lines changed: 97 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,18 @@
6060
#define LPC_ODR4 0x118
6161
#define LPC_STR4 0x11C
6262

63+
#define OBE_POLL_PERIOD (HZ / 2)
64+
6365
struct aspeed_kcs_bmc {
6466
struct kcs_bmc_device kcs_bmc;
6567

6668
struct regmap *map;
69+
70+
struct {
71+
spinlock_t lock;
72+
bool remove;
73+
struct timer_list timer;
74+
} obe;
6775
};
6876

6977
struct aspeed_kcs_of_ops {
@@ -159,68 +167,89 @@ static void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enabl
159167

160168
switch (kcs_bmc->channel) {
161169
case 1:
162-
if (enable) {
163-
regmap_update_bits(priv->map, LPC_HICR2,
164-
LPC_HICR2_IBFIF1, LPC_HICR2_IBFIF1);
165-
regmap_update_bits(priv->map, LPC_HICR0,
166-
LPC_HICR0_LPC1E, LPC_HICR0_LPC1E);
167-
} else {
168-
regmap_update_bits(priv->map, LPC_HICR0,
169-
LPC_HICR0_LPC1E, 0);
170-
regmap_update_bits(priv->map, LPC_HICR2,
171-
LPC_HICR2_IBFIF1, 0);
172-
}
173-
break;
174-
170+
regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC1E, enable * LPC_HICR0_LPC1E);
171+
return;
175172
case 2:
176-
if (enable) {
177-
regmap_update_bits(priv->map, LPC_HICR2,
178-
LPC_HICR2_IBFIF2, LPC_HICR2_IBFIF2);
179-
regmap_update_bits(priv->map, LPC_HICR0,
180-
LPC_HICR0_LPC2E, LPC_HICR0_LPC2E);
181-
} else {
182-
regmap_update_bits(priv->map, LPC_HICR0,
183-
LPC_HICR0_LPC2E, 0);
184-
regmap_update_bits(priv->map, LPC_HICR2,
185-
LPC_HICR2_IBFIF2, 0);
186-
}
187-
break;
188-
173+
regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC2E, enable * LPC_HICR0_LPC2E);
174+
return;
189175
case 3:
190-
if (enable) {
191-
regmap_update_bits(priv->map, LPC_HICR2,
192-
LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3);
193-
regmap_update_bits(priv->map, LPC_HICR0,
194-
LPC_HICR0_LPC3E, LPC_HICR0_LPC3E);
195-
regmap_update_bits(priv->map, LPC_HICR4,
196-
LPC_HICR4_KCSENBL, LPC_HICR4_KCSENBL);
197-
} else {
198-
regmap_update_bits(priv->map, LPC_HICR0,
199-
LPC_HICR0_LPC3E, 0);
200-
regmap_update_bits(priv->map, LPC_HICR4,
201-
LPC_HICR4_KCSENBL, 0);
202-
regmap_update_bits(priv->map, LPC_HICR2,
203-
LPC_HICR2_IBFIF3, 0);
204-
}
205-
break;
206-
176+
regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC3E, enable * LPC_HICR0_LPC3E);
177+
regmap_update_bits(priv->map, LPC_HICR4,
178+
LPC_HICR4_KCSENBL, enable * LPC_HICR4_KCSENBL);
179+
return;
207180
case 4:
208-
if (enable)
209-
regmap_update_bits(priv->map, LPC_HICRB,
210-
LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
211-
LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E);
181+
regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_LPC4E, enable * LPC_HICRB_LPC4E);
182+
return;
183+
default:
184+
pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
185+
return;
186+
}
187+
}
188+
189+
static void aspeed_kcs_check_obe(struct timer_list *timer)
190+
{
191+
struct aspeed_kcs_bmc *priv = container_of(timer, struct aspeed_kcs_bmc, obe.timer);
192+
unsigned long flags;
193+
u8 str;
194+
195+
spin_lock_irqsave(&priv->obe.lock, flags);
196+
if (priv->obe.remove) {
197+
spin_unlock_irqrestore(&priv->obe.lock, flags);
198+
return;
199+
}
200+
201+
str = aspeed_kcs_inb(&priv->kcs_bmc, priv->kcs_bmc.ioreg.str);
202+
if (str & KCS_BMC_STR_OBF) {
203+
mod_timer(timer, jiffies + OBE_POLL_PERIOD);
204+
spin_unlock_irqrestore(&priv->obe.lock, flags);
205+
return;
206+
}
207+
spin_unlock_irqrestore(&priv->obe.lock, flags);
208+
209+
kcs_bmc_handle_event(&priv->kcs_bmc);
210+
}
211+
212+
static void aspeed_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
213+
{
214+
struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
215+
216+
/* We don't have an OBE IRQ, emulate it */
217+
if (mask & KCS_BMC_EVENT_TYPE_OBE) {
218+
if (KCS_BMC_EVENT_TYPE_OBE & state)
219+
mod_timer(&priv->obe.timer, jiffies + OBE_POLL_PERIOD);
212220
else
213-
regmap_update_bits(priv->map, LPC_HICRB,
214-
LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E,
215-
0);
216-
break;
221+
del_timer(&priv->obe.timer);
222+
}
217223

218-
default:
219-
break;
224+
if (mask & KCS_BMC_EVENT_TYPE_IBF) {
225+
const bool enable = !!(state & KCS_BMC_EVENT_TYPE_IBF);
226+
227+
switch (kcs_bmc->channel) {
228+
case 1:
229+
regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF1,
230+
enable * LPC_HICR2_IBFIF1);
231+
return;
232+
case 2:
233+
regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF2,
234+
enable * LPC_HICR2_IBFIF2);
235+
return;
236+
case 3:
237+
regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIF3,
238+
enable * LPC_HICR2_IBFIF3);
239+
return;
240+
case 4:
241+
regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIF4,
242+
enable * LPC_HICRB_IBFIF4);
243+
return;
244+
default:
245+
pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
246+
return;
247+
}
220248
}
221249
}
222250

223251
static const struct kcs_bmc_device_ops aspeed_kcs_ops = {
252+
.irq_mask_update = aspeed_kcs_irq_mask_update,
224253
.io_inputb = aspeed_kcs_inb,
225254
.io_outputb = aspeed_kcs_outb,
226255
.io_updateb = aspeed_kcs_updateb,
@@ -375,6 +404,10 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
375404
return -ENODEV;
376405
}
377406

407+
spin_lock_init(&priv->obe.lock);
408+
priv->obe.remove = false;
409+
timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
410+
378411
aspeed_kcs_set_address(kcs_bmc, addr);
379412

380413
rc = aspeed_kcs_config_irq(kcs_bmc, pdev);
@@ -383,6 +416,8 @@ static int aspeed_kcs_probe(struct platform_device *pdev)
383416

384417
platform_set_drvdata(pdev, priv);
385418

419+
aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
420+
KCS_BMC_EVENT_TYPE_IBF);
386421
aspeed_kcs_enable_channel(kcs_bmc, true);
387422

388423
rc = kcs_bmc_add_device(&priv->kcs_bmc);
@@ -403,6 +438,15 @@ static int aspeed_kcs_remove(struct platform_device *pdev)
403438

404439
kcs_bmc_remove_device(kcs_bmc);
405440

441+
aspeed_kcs_enable_channel(kcs_bmc, false);
442+
aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
443+
444+
/* Make sure it's proper dead */
445+
spin_lock_irq(&priv->obe.lock);
446+
priv->obe.remove = true;
447+
spin_unlock_irq(&priv->obe.lock);
448+
del_timer_sync(&priv->obe.timer);
449+
406450
return 0;
407451
}
408452

drivers/char/ipmi/kcs_bmc_client.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ void kcs_bmc_unregister_driver(struct kcs_bmc_driver *drv);
3535
int kcs_bmc_enable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
3636
void kcs_bmc_disable_device(struct kcs_bmc_device *kcs_bmc, struct kcs_bmc_client *client);
3737

38+
void kcs_bmc_update_event_mask(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 events);
39+
3840
u8 kcs_bmc_read_data(struct kcs_bmc_device *kcs_bmc);
3941
void kcs_bmc_write_data(struct kcs_bmc_device *kcs_bmc, u8 data);
4042
u8 kcs_bmc_read_status(struct kcs_bmc_device *kcs_bmc);

drivers/char/ipmi/kcs_bmc_device.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "kcs_bmc.h"
1010

1111
struct kcs_bmc_device_ops {
12+
void (*irq_mask_update)(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 enable);
1213
u8 (*io_inputb)(struct kcs_bmc_device *kcs_bmc, u32 reg);
1314
void (*io_outputb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 b);
1415
void (*io_updateb)(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 b);

drivers/char/ipmi/kcs_bmc_npcm7xx.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#define KCS2CTL 0x2A
3939
#define KCS3CTL 0x3C
4040
#define KCS_CTL_IBFIE BIT(0)
41+
#define KCS_CTL_OBEIE BIT(1)
4142

4243
#define KCS1IE 0x1C
4344
#define KCS2IE 0x2E
@@ -117,13 +118,23 @@ static void npcm7xx_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enab
117118
{
118119
struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
119120

120-
regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
121-
enable ? KCS_CTL_IBFIE : 0);
122-
123121
regmap_update_bits(priv->map, priv->reg->ie, KCS_IE_IRQE | KCS_IE_HIRQE,
124122
enable ? KCS_IE_IRQE | KCS_IE_HIRQE : 0);
125123
}
126124

125+
static void npcm7xx_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
126+
{
127+
struct npcm7xx_kcs_bmc *priv = to_npcm7xx_kcs_bmc(kcs_bmc);
128+
129+
if (mask & KCS_BMC_EVENT_TYPE_OBE)
130+
regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_OBEIE,
131+
!!(state & KCS_BMC_EVENT_TYPE_OBE) * KCS_CTL_OBEIE);
132+
133+
if (mask & KCS_BMC_EVENT_TYPE_IBF)
134+
regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
135+
!!(state & KCS_BMC_EVENT_TYPE_IBF) * KCS_CTL_IBFIE);
136+
}
137+
127138
static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
128139
{
129140
struct kcs_bmc_device *kcs_bmc = arg;
@@ -146,6 +157,7 @@ static int npcm7xx_kcs_config_irq(struct kcs_bmc_device *kcs_bmc,
146157
}
147158

148159
static const struct kcs_bmc_device_ops npcm7xx_kcs_ops = {
160+
.irq_mask_update = npcm7xx_kcs_irq_mask_update,
149161
.io_inputb = npcm7xx_kcs_inb,
150162
.io_outputb = npcm7xx_kcs_outb,
151163
.io_updateb = npcm7xx_kcs_updateb,
@@ -186,11 +198,14 @@ static int npcm7xx_kcs_probe(struct platform_device *pdev)
186198

187199
platform_set_drvdata(pdev, priv);
188200

189-
npcm7xx_kcs_enable_channel(kcs_bmc, true);
190201
rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
191202
if (rc)
192203
return rc;
193204

205+
npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE),
206+
KCS_BMC_EVENT_TYPE_IBF);
207+
npcm7xx_kcs_enable_channel(kcs_bmc, true);
208+
194209
rc = kcs_bmc_add_device(kcs_bmc);
195210
if (rc) {
196211
dev_warn(&pdev->dev, "Failed to register channel %d: %d\n", kcs_bmc->channel, rc);
@@ -211,6 +226,9 @@ static int npcm7xx_kcs_remove(struct platform_device *pdev)
211226

212227
kcs_bmc_remove_device(kcs_bmc);
213228

229+
npcm7xx_kcs_enable_channel(kcs_bmc, false);
230+
npcm7xx_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
231+
214232
return 0;
215233
}
216234

0 commit comments

Comments
 (0)