]>
Commit | Line | Data |
---|---|---|
cc20a386 KD |
1 | /* |
2 | * Copyright 2016 Broadcom Limited | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/device.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/ioport.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/of_address.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/slab.h> | |
22 | ||
23 | #include "spi-bcm-qspi.h" | |
24 | ||
25 | #define INTR_BASE_BIT_SHIFT 0x02 | |
26 | #define INTR_COUNT 0x07 | |
27 | ||
28 | struct bcm_iproc_intc { | |
29 | struct bcm_qspi_soc_intc soc_intc; | |
30 | struct platform_device *pdev; | |
31 | void __iomem *int_reg; | |
32 | void __iomem *int_status_reg; | |
33 | spinlock_t soclock; | |
34 | bool big_endian; | |
35 | }; | |
36 | ||
37 | static u32 bcm_iproc_qspi_get_l2_int_status(struct bcm_qspi_soc_intc *soc_intc) | |
38 | { | |
39 | struct bcm_iproc_intc *priv = | |
40 | container_of(soc_intc, struct bcm_iproc_intc, soc_intc); | |
41 | void __iomem *mmio = priv->int_status_reg; | |
42 | int i; | |
43 | u32 val = 0, sts = 0; | |
44 | ||
45 | for (i = 0; i < INTR_COUNT; i++) { | |
46 | if (bcm_qspi_readl(priv->big_endian, mmio + (i * 4))) | |
47 | val |= 1UL << i; | |
48 | } | |
49 | ||
50 | if (val & INTR_MSPI_DONE_MASK) | |
51 | sts |= MSPI_DONE; | |
52 | ||
53 | if (val & BSPI_LR_INTERRUPTS_ALL) | |
54 | sts |= BSPI_DONE; | |
55 | ||
56 | if (val & BSPI_LR_INTERRUPTS_ERROR) | |
57 | sts |= BSPI_ERR; | |
58 | ||
59 | return sts; | |
60 | } | |
61 | ||
62 | static void bcm_iproc_qspi_int_ack(struct bcm_qspi_soc_intc *soc_intc, int type) | |
63 | { | |
64 | struct bcm_iproc_intc *priv = | |
65 | container_of(soc_intc, struct bcm_iproc_intc, soc_intc); | |
66 | void __iomem *mmio = priv->int_status_reg; | |
67 | u32 mask = get_qspi_mask(type); | |
68 | int i; | |
69 | ||
70 | for (i = 0; i < INTR_COUNT; i++) { | |
71 | if (mask & (1UL << i)) | |
72 | bcm_qspi_writel(priv->big_endian, 1, mmio + (i * 4)); | |
73 | } | |
74 | } | |
75 | ||
76 | static void bcm_iproc_qspi_int_set(struct bcm_qspi_soc_intc *soc_intc, int type, | |
77 | bool en) | |
78 | { | |
79 | struct bcm_iproc_intc *priv = | |
80 | container_of(soc_intc, struct bcm_iproc_intc, soc_intc); | |
81 | void __iomem *mmio = priv->int_reg; | |
82 | u32 mask = get_qspi_mask(type); | |
83 | u32 val; | |
84 | unsigned long flags; | |
85 | ||
86 | spin_lock_irqsave(&priv->soclock, flags); | |
87 | ||
88 | val = bcm_qspi_readl(priv->big_endian, mmio); | |
89 | ||
90 | if (en) | |
91 | val = val | (mask << INTR_BASE_BIT_SHIFT); | |
92 | else | |
93 | val = val & ~(mask << INTR_BASE_BIT_SHIFT); | |
94 | ||
95 | bcm_qspi_writel(priv->big_endian, val, mmio); | |
96 | ||
97 | spin_unlock_irqrestore(&priv->soclock, flags); | |
98 | } | |
99 | ||
100 | static int bcm_iproc_probe(struct platform_device *pdev) | |
101 | { | |
102 | struct device *dev = &pdev->dev; | |
103 | struct bcm_iproc_intc *priv; | |
104 | struct bcm_qspi_soc_intc *soc_intc; | |
105 | struct resource *res; | |
106 | ||
107 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
108 | if (!priv) | |
109 | return -ENOMEM; | |
110 | soc_intc = &priv->soc_intc; | |
111 | priv->pdev = pdev; | |
112 | ||
113 | spin_lock_init(&priv->soclock); | |
114 | ||
115 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr_regs"); | |
116 | priv->int_reg = devm_ioremap_resource(dev, res); | |
117 | if (IS_ERR(priv->int_reg)) | |
118 | return PTR_ERR(priv->int_reg); | |
119 | ||
120 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
121 | "intr_status_reg"); | |
122 | priv->int_status_reg = devm_ioremap_resource(dev, res); | |
123 | if (IS_ERR(priv->int_status_reg)) | |
124 | return PTR_ERR(priv->int_status_reg); | |
125 | ||
126 | priv->big_endian = of_device_is_big_endian(dev->of_node); | |
127 | ||
128 | bcm_iproc_qspi_int_ack(soc_intc, MSPI_BSPI_DONE); | |
129 | bcm_iproc_qspi_int_set(soc_intc, MSPI_BSPI_DONE, false); | |
130 | ||
131 | soc_intc->bcm_qspi_int_ack = bcm_iproc_qspi_int_ack; | |
132 | soc_intc->bcm_qspi_int_set = bcm_iproc_qspi_int_set; | |
133 | soc_intc->bcm_qspi_get_int_status = bcm_iproc_qspi_get_l2_int_status; | |
134 | ||
135 | return bcm_qspi_probe(pdev, soc_intc); | |
136 | } | |
137 | ||
138 | static int bcm_iproc_remove(struct platform_device *pdev) | |
139 | { | |
140 | return bcm_qspi_remove(pdev); | |
141 | } | |
142 | ||
143 | static const struct of_device_id bcm_iproc_of_match[] = { | |
144 | { .compatible = "brcm,spi-nsp-qspi" }, | |
145 | { .compatible = "brcm,spi-ns2-qspi" }, | |
146 | {}, | |
147 | }; | |
148 | MODULE_DEVICE_TABLE(of, bcm_iproc_of_match); | |
149 | ||
150 | static struct platform_driver bcm_iproc_driver = { | |
151 | .probe = bcm_iproc_probe, | |
152 | .remove = bcm_iproc_remove, | |
153 | .driver = { | |
154 | .name = "bcm_iproc", | |
155 | .pm = &bcm_qspi_pm_ops, | |
156 | .of_match_table = bcm_iproc_of_match, | |
157 | } | |
158 | }; | |
159 | module_platform_driver(bcm_iproc_driver); | |
160 | ||
161 | MODULE_LICENSE("GPL v2"); | |
162 | MODULE_AUTHOR("Kamal Dasu"); | |
163 | MODULE_DESCRIPTION("SPI flash driver for Broadcom iProc SoCs"); |