summaryrefslogtreecommitdiff
path: root/drivers/soc/aspeed/aspeed-lpc-snoop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc/aspeed/aspeed-lpc-snoop.c')
-rw-r--r--drivers/soc/aspeed/aspeed-lpc-snoop.c225
1 files changed, 105 insertions, 120 deletions
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index fc3a2c41cc10..b03310c0830d 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -12,6 +12,7 @@
#include <linux/bitops.h>
#include <linux/clk.h>
+#include <linux/dev_printk.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/kfifo.h>
@@ -25,7 +26,6 @@
#define DEVICE_NAME "aspeed-lpc-snoop"
-#define NUM_SNOOP_CHANNELS 2
#define SNOOP_FIFO_SIZE 2048
#define HICR5 0x80
@@ -57,7 +57,22 @@ struct aspeed_lpc_snoop_model_data {
unsigned int has_hicrb_ensnp;
};
+enum aspeed_lpc_snoop_index {
+ ASPEED_LPC_SNOOP_INDEX_0 = 0,
+ ASPEED_LPC_SNOOP_INDEX_1 = 1,
+ ASPEED_LPC_SNOOP_INDEX_MAX = ASPEED_LPC_SNOOP_INDEX_1,
+};
+
+struct aspeed_lpc_snoop_channel_cfg {
+ enum aspeed_lpc_snoop_index index;
+ u32 hicr5_en;
+ u32 snpwadr_mask;
+ u32 snpwadr_shift;
+ u32 hicrb_en;
+};
+
struct aspeed_lpc_snoop_channel {
+ const struct aspeed_lpc_snoop_channel_cfg *cfg;
bool enabled;
struct kfifo fifo;
wait_queue_head_t wq;
@@ -68,7 +83,24 @@ struct aspeed_lpc_snoop {
struct regmap *regmap;
int irq;
struct clk *clk;
- struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS];
+ struct aspeed_lpc_snoop_channel chan[ASPEED_LPC_SNOOP_INDEX_MAX + 1];
+};
+
+static const struct aspeed_lpc_snoop_channel_cfg channel_cfgs[ASPEED_LPC_SNOOP_INDEX_MAX + 1] = {
+ {
+ .index = ASPEED_LPC_SNOOP_INDEX_0,
+ .hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W,
+ .snpwadr_mask = SNPWADR_CH0_MASK,
+ .snpwadr_shift = SNPWADR_CH0_SHIFT,
+ .hicrb_en = HICRB_ENSNP0D,
+ },
+ {
+ .index = ASPEED_LPC_SNOOP_INDEX_1,
+ .hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W,
+ .snpwadr_mask = SNPWADR_CH1_MASK,
+ .snpwadr_shift = SNPWADR_CH1_SHIFT,
+ .hicrb_en = HICRB_ENSNP1D,
+ },
};
static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file)
@@ -182,108 +214,88 @@ static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop,
return 0;
}
-static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
- struct device *dev,
- int channel, u16 lpc_port)
+__attribute__((nonnull))
+static int aspeed_lpc_enable_snoop(struct device *dev,
+ struct aspeed_lpc_snoop *lpc_snoop,
+ struct aspeed_lpc_snoop_channel *channel,
+ const struct aspeed_lpc_snoop_channel_cfg *cfg,
+ u16 lpc_port)
{
+ const struct aspeed_lpc_snoop_model_data *model_data;
int rc = 0;
- u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en;
- const struct aspeed_lpc_snoop_model_data *model_data =
- of_device_get_match_data(dev);
- if (WARN_ON(lpc_snoop->chan[channel].enabled))
+ if (WARN_ON(channel->enabled))
return -EBUSY;
- init_waitqueue_head(&lpc_snoop->chan[channel].wq);
- /* Create FIFO datastructure */
- rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo,
- SNOOP_FIFO_SIZE, GFP_KERNEL);
+ init_waitqueue_head(&channel->wq);
+
+ channel->cfg = cfg;
+ channel->miscdev.minor = MISC_DYNAMIC_MINOR;
+ channel->miscdev.fops = &snoop_fops;
+ channel->miscdev.parent = dev;
+
+ channel->miscdev.name =
+ devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, cfg->index);
+ if (!channel->miscdev.name)
+ return -ENOMEM;
+
+ rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
if (rc)
return rc;
- lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
- lpc_snoop->chan[channel].miscdev.name =
- devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
- if (!lpc_snoop->chan[channel].miscdev.name) {
- rc = -ENOMEM;
- goto err_free_fifo;
- }
- lpc_snoop->chan[channel].miscdev.fops = &snoop_fops;
- lpc_snoop->chan[channel].miscdev.parent = dev;
- rc = misc_register(&lpc_snoop->chan[channel].miscdev);
+ rc = misc_register(&channel->miscdev);
if (rc)
goto err_free_fifo;
/* Enable LPC snoop channel at requested port */
- switch (channel) {
- case 0:
- hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W;
- snpwadr_mask = SNPWADR_CH0_MASK;
- snpwadr_shift = SNPWADR_CH0_SHIFT;
- hicrb_en = HICRB_ENSNP0D;
- break;
- case 1:
- hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W;
- snpwadr_mask = SNPWADR_CH1_MASK;
- snpwadr_shift = SNPWADR_CH1_SHIFT;
- hicrb_en = HICRB_ENSNP1D;
- break;
- default:
- rc = -EINVAL;
- goto err_misc_deregister;
- }
+ regmap_set_bits(lpc_snoop->regmap, HICR5, cfg->hicr5_en);
+ regmap_update_bits(lpc_snoop->regmap, SNPWADR, cfg->snpwadr_mask,
+ lpc_port << cfg->snpwadr_shift);
- regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en);
- regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask,
- lpc_port << snpwadr_shift);
- if (model_data->has_hicrb_ensnp)
- regmap_update_bits(lpc_snoop->regmap, HICRB,
- hicrb_en, hicrb_en);
+ model_data = of_device_get_match_data(dev);
+ if (model_data && model_data->has_hicrb_ensnp)
+ regmap_set_bits(lpc_snoop->regmap, HICRB, cfg->hicrb_en);
- lpc_snoop->chan[channel].enabled = true;
+ channel->enabled = true;
return 0;
-err_misc_deregister:
- misc_deregister(&lpc_snoop->chan[channel].miscdev);
err_free_fifo:
- kfifo_free(&lpc_snoop->chan[channel].fifo);
+ kfifo_free(&channel->fifo);
return rc;
}
+__attribute__((nonnull))
static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
- int channel)
+ struct aspeed_lpc_snoop_channel *channel)
{
- if (!lpc_snoop->chan[channel].enabled)
+ if (!channel->enabled)
return;
- switch (channel) {
- case 0:
- regmap_update_bits(lpc_snoop->regmap, HICR5,
- HICR5_EN_SNP0W | HICR5_ENINT_SNP0W,
- 0);
- break;
- case 1:
- regmap_update_bits(lpc_snoop->regmap, HICR5,
- HICR5_EN_SNP1W | HICR5_ENINT_SNP1W,
- 0);
- break;
- default:
- return;
- }
+ /* Disable interrupts along with the device */
+ regmap_clear_bits(lpc_snoop->regmap, HICR5, channel->cfg->hicr5_en);
- lpc_snoop->chan[channel].enabled = false;
+ channel->enabled = false;
/* Consider improving safety wrt concurrent reader(s) */
- misc_deregister(&lpc_snoop->chan[channel].miscdev);
- kfifo_free(&lpc_snoop->chan[channel].fifo);
+ misc_deregister(&channel->miscdev);
+ kfifo_free(&channel->fifo);
+}
+
+static void aspeed_lpc_snoop_remove(struct platform_device *pdev)
+{
+ struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev);
+
+ /* Disable both snoop channels */
+ aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[0]);
+ aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[1]);
}
static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
{
struct aspeed_lpc_snoop *lpc_snoop;
- struct device *dev;
struct device_node *np;
- u32 port;
+ struct device *dev;
+ int idx;
int rc;
dev = &pdev->dev;
@@ -301,67 +313,40 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
}
lpc_snoop->regmap = syscon_node_to_regmap(np);
- if (IS_ERR(lpc_snoop->regmap)) {
- dev_err(dev, "Couldn't get regmap\n");
- return -ENODEV;
- }
+ if (IS_ERR(lpc_snoop->regmap))
+ return dev_err_probe(dev, PTR_ERR(lpc_snoop->regmap), "Couldn't get regmap\n");
dev_set_drvdata(&pdev->dev, lpc_snoop);
- rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port);
- if (rc) {
- dev_err(dev, "no snoop ports configured\n");
- return -ENODEV;
- }
-
- lpc_snoop->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(lpc_snoop->clk)) {
- rc = PTR_ERR(lpc_snoop->clk);
- if (rc != -EPROBE_DEFER)
- dev_err(dev, "couldn't get clock\n");
- return rc;
- }
- rc = clk_prepare_enable(lpc_snoop->clk);
- if (rc) {
- dev_err(dev, "couldn't enable clock\n");
- return rc;
- }
+ lpc_snoop->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(lpc_snoop->clk))
+ return dev_err_probe(dev, PTR_ERR(lpc_snoop->clk), "couldn't get clock");
rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev);
if (rc)
- goto err;
-
- rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port);
- if (rc)
- goto err;
-
- /* Configuration of 2nd snoop channel port is optional */
- if (of_property_read_u32_index(dev->of_node, "snoop-ports",
- 1, &port) == 0) {
- rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port);
- if (rc) {
- aspeed_lpc_disable_snoop(lpc_snoop, 0);
- goto err;
- }
- }
+ return rc;
- return 0;
+ static_assert(ARRAY_SIZE(channel_cfgs) == ARRAY_SIZE(lpc_snoop->chan),
+ "Broken implementation assumption regarding cfg count");
+ for (idx = ASPEED_LPC_SNOOP_INDEX_0; idx <= ASPEED_LPC_SNOOP_INDEX_MAX; idx++) {
+ u32 port;
-err:
- clk_disable_unprepare(lpc_snoop->clk);
+ rc = of_property_read_u32_index(dev->of_node, "snoop-ports", idx, &port);
+ if (rc)
+ break;
- return rc;
-}
+ rc = aspeed_lpc_enable_snoop(dev, lpc_snoop, &lpc_snoop->chan[idx],
+ &channel_cfgs[idx], port);
+ if (rc)
+ goto cleanup_channels;
+ }
-static void aspeed_lpc_snoop_remove(struct platform_device *pdev)
-{
- struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev);
+ return idx == ASPEED_LPC_SNOOP_INDEX_0 ? -ENODEV : 0;
- /* Disable both snoop channels */
- aspeed_lpc_disable_snoop(lpc_snoop, 0);
- aspeed_lpc_disable_snoop(lpc_snoop, 1);
+cleanup_channels:
+ aspeed_lpc_snoop_remove(pdev);
- clk_disable_unprepare(lpc_snoop->clk);
+ return rc;
}
static const struct aspeed_lpc_snoop_model_data ast2400_model_data = {