1212
1313#include <linux/bitops.h>
1414#include <linux/clk.h>
15+ #include <linux/dev_printk.h>
1516#include <linux/interrupt.h>
1617#include <linux/fs.h>
1718#include <linux/kfifo.h>
2526
2627#define DEVICE_NAME "aspeed-lpc-snoop"
2728
28- #define NUM_SNOOP_CHANNELS 2
2929#define SNOOP_FIFO_SIZE 2048
3030
3131#define HICR5 0x80
@@ -57,7 +57,23 @@ struct aspeed_lpc_snoop_model_data {
5757 unsigned int has_hicrb_ensnp ;
5858};
5959
60+ enum aspeed_lpc_snoop_index {
61+ ASPEED_LPC_SNOOP_INDEX_0 = 0 ,
62+ ASPEED_LPC_SNOOP_INDEX_1 = 1 ,
63+ ASPEED_LPC_SNOOP_INDEX_MAX = ASPEED_LPC_SNOOP_INDEX_1 ,
64+ };
65+
66+ struct aspeed_lpc_snoop_channel_cfg {
67+ enum aspeed_lpc_snoop_index index ;
68+ u32 hicr5_en ;
69+ u32 snpwadr_mask ;
70+ u32 snpwadr_shift ;
71+ u32 hicrb_en ;
72+ };
73+
6074struct aspeed_lpc_snoop_channel {
75+ const struct aspeed_lpc_snoop_channel_cfg * cfg ;
76+ bool enabled ;
6177 struct kfifo fifo ;
6278 wait_queue_head_t wq ;
6379 struct miscdevice miscdev ;
@@ -67,7 +83,24 @@ struct aspeed_lpc_snoop {
6783 struct regmap * regmap ;
6884 int irq ;
6985 struct clk * clk ;
70- struct aspeed_lpc_snoop_channel chan [NUM_SNOOP_CHANNELS ];
86+ struct aspeed_lpc_snoop_channel chan [ASPEED_LPC_SNOOP_INDEX_MAX + 1 ];
87+ };
88+
89+ static const struct aspeed_lpc_snoop_channel_cfg channel_cfgs [ASPEED_LPC_SNOOP_INDEX_MAX + 1 ] = {
90+ {
91+ .index = ASPEED_LPC_SNOOP_INDEX_0 ,
92+ .hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W ,
93+ .snpwadr_mask = SNPWADR_CH0_MASK ,
94+ .snpwadr_shift = SNPWADR_CH0_SHIFT ,
95+ .hicrb_en = HICRB_ENSNP0D ,
96+ },
97+ {
98+ .index = ASPEED_LPC_SNOOP_INDEX_1 ,
99+ .hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W ,
100+ .snpwadr_mask = SNPWADR_CH1_MASK ,
101+ .snpwadr_shift = SNPWADR_CH1_SHIFT ,
102+ .hicrb_en = HICRB_ENSNP1D ,
103+ },
71104};
72105
73106static struct aspeed_lpc_snoop_channel * snoop_file_to_chan (struct file * file )
@@ -181,98 +214,88 @@ static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop,
181214 return 0 ;
182215}
183216
184- static int aspeed_lpc_enable_snoop (struct aspeed_lpc_snoop * lpc_snoop ,
185- struct device * dev ,
186- int channel , u16 lpc_port )
217+ __attribute__((nonnull ))
218+ static int aspeed_lpc_enable_snoop (struct device * dev ,
219+ struct aspeed_lpc_snoop * lpc_snoop ,
220+ struct aspeed_lpc_snoop_channel * channel ,
221+ const struct aspeed_lpc_snoop_channel_cfg * cfg ,
222+ u16 lpc_port )
187223{
224+ const struct aspeed_lpc_snoop_model_data * model_data ;
188225 int rc = 0 ;
189- u32 hicr5_en , snpwadr_mask , snpwadr_shift , hicrb_en ;
190- const struct aspeed_lpc_snoop_model_data * model_data =
191- of_device_get_match_data (dev );
192-
193- init_waitqueue_head (& lpc_snoop -> chan [channel ].wq );
194- /* Create FIFO datastructure */
195- rc = kfifo_alloc (& lpc_snoop -> chan [channel ].fifo ,
196- SNOOP_FIFO_SIZE , GFP_KERNEL );
226+
227+ if (WARN_ON (channel -> enabled ))
228+ return - EBUSY ;
229+
230+ init_waitqueue_head (& channel -> wq );
231+
232+ channel -> cfg = cfg ;
233+ channel -> miscdev .minor = MISC_DYNAMIC_MINOR ;
234+ channel -> miscdev .fops = & snoop_fops ;
235+ channel -> miscdev .parent = dev ;
236+
237+ channel -> miscdev .name =
238+ devm_kasprintf (dev , GFP_KERNEL , "%s%d" , DEVICE_NAME , cfg -> index );
239+ if (!channel -> miscdev .name )
240+ return - ENOMEM ;
241+
242+ rc = kfifo_alloc (& channel -> fifo , SNOOP_FIFO_SIZE , GFP_KERNEL );
197243 if (rc )
198244 return rc ;
199245
200- lpc_snoop -> chan [channel ].miscdev .minor = MISC_DYNAMIC_MINOR ;
201- lpc_snoop -> chan [channel ].miscdev .name =
202- devm_kasprintf (dev , GFP_KERNEL , "%s%d" , DEVICE_NAME , channel );
203- if (!lpc_snoop -> chan [channel ].miscdev .name ) {
204- rc = - ENOMEM ;
205- goto err_free_fifo ;
206- }
207- lpc_snoop -> chan [channel ].miscdev .fops = & snoop_fops ;
208- lpc_snoop -> chan [channel ].miscdev .parent = dev ;
209- rc = misc_register (& lpc_snoop -> chan [channel ].miscdev );
246+ rc = misc_register (& channel -> miscdev );
210247 if (rc )
211248 goto err_free_fifo ;
212249
213250 /* Enable LPC snoop channel at requested port */
214- switch (channel ) {
215- case 0 :
216- hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W ;
217- snpwadr_mask = SNPWADR_CH0_MASK ;
218- snpwadr_shift = SNPWADR_CH0_SHIFT ;
219- hicrb_en = HICRB_ENSNP0D ;
220- break ;
221- case 1 :
222- hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W ;
223- snpwadr_mask = SNPWADR_CH1_MASK ;
224- snpwadr_shift = SNPWADR_CH1_SHIFT ;
225- hicrb_en = HICRB_ENSNP1D ;
226- break ;
227- default :
228- rc = - EINVAL ;
229- goto err_misc_deregister ;
230- }
251+ regmap_set_bits (lpc_snoop -> regmap , HICR5 , cfg -> hicr5_en );
252+ regmap_update_bits (lpc_snoop -> regmap , SNPWADR , cfg -> snpwadr_mask ,
253+ lpc_port << cfg -> snpwadr_shift );
231254
232- regmap_update_bits (lpc_snoop -> regmap , HICR5 , hicr5_en , hicr5_en );
233- regmap_update_bits (lpc_snoop -> regmap , SNPWADR , snpwadr_mask ,
234- lpc_port << snpwadr_shift );
235- if (model_data -> has_hicrb_ensnp )
236- regmap_update_bits (lpc_snoop -> regmap , HICRB ,
237- hicrb_en , hicrb_en );
255+ model_data = of_device_get_match_data (dev );
256+ if (model_data && model_data -> has_hicrb_ensnp )
257+ regmap_set_bits (lpc_snoop -> regmap , HICRB , cfg -> hicrb_en );
258+
259+ channel -> enabled = true;
238260
239261 return 0 ;
240262
241- err_misc_deregister :
242- misc_deregister (& lpc_snoop -> chan [channel ].miscdev );
243263err_free_fifo :
244- kfifo_free (& lpc_snoop -> chan [ channel ]. fifo );
264+ kfifo_free (& channel -> fifo );
245265 return rc ;
246266}
247267
268+ __attribute__((nonnull ))
248269static void aspeed_lpc_disable_snoop (struct aspeed_lpc_snoop * lpc_snoop ,
249- int channel )
270+ struct aspeed_lpc_snoop_channel * channel )
250271{
251- switch (channel ) {
252- case 0 :
253- regmap_update_bits (lpc_snoop -> regmap , HICR5 ,
254- HICR5_EN_SNP0W | HICR5_ENINT_SNP0W ,
255- 0 );
256- break ;
257- case 1 :
258- regmap_update_bits (lpc_snoop -> regmap , HICR5 ,
259- HICR5_EN_SNP1W | HICR5_ENINT_SNP1W ,
260- 0 );
261- break ;
262- default :
272+ if (!channel -> enabled )
263273 return ;
264- }
265274
266- kfifo_free (& lpc_snoop -> chan [channel ].fifo );
267- misc_deregister (& lpc_snoop -> chan [channel ].miscdev );
275+ /* Disable interrupts along with the device */
276+ regmap_clear_bits (lpc_snoop -> regmap , HICR5 , channel -> cfg -> hicr5_en );
277+
278+ channel -> enabled = false;
279+ /* Consider improving safety wrt concurrent reader(s) */
280+ misc_deregister (& channel -> miscdev );
281+ kfifo_free (& channel -> fifo );
282+ }
283+
284+ static void aspeed_lpc_snoop_remove (struct platform_device * pdev )
285+ {
286+ struct aspeed_lpc_snoop * lpc_snoop = dev_get_drvdata (& pdev -> dev );
287+
288+ /* Disable both snoop channels */
289+ aspeed_lpc_disable_snoop (lpc_snoop , & lpc_snoop -> chan [0 ]);
290+ aspeed_lpc_disable_snoop (lpc_snoop , & lpc_snoop -> chan [1 ]);
268291}
269292
270293static int aspeed_lpc_snoop_probe (struct platform_device * pdev )
271294{
272295 struct aspeed_lpc_snoop * lpc_snoop ;
273- struct device * dev ;
274296 struct device_node * np ;
275- u32 port ;
297+ struct device * dev ;
298+ int idx ;
276299 int rc ;
277300
278301 dev = & pdev -> dev ;
@@ -290,67 +313,40 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
290313 }
291314
292315 lpc_snoop -> regmap = syscon_node_to_regmap (np );
293- if (IS_ERR (lpc_snoop -> regmap )) {
294- dev_err (dev , "Couldn't get regmap\n" );
295- return - ENODEV ;
296- }
316+ if (IS_ERR (lpc_snoop -> regmap ))
317+ return dev_err_probe (dev , PTR_ERR (lpc_snoop -> regmap ), "Couldn't get regmap\n" );
297318
298319 dev_set_drvdata (& pdev -> dev , lpc_snoop );
299320
300- rc = of_property_read_u32_index (dev -> of_node , "snoop-ports" , 0 , & port );
301- if (rc ) {
302- dev_err (dev , "no snoop ports configured\n" );
303- return - ENODEV ;
304- }
305-
306- lpc_snoop -> clk = devm_clk_get (dev , NULL );
307- if (IS_ERR (lpc_snoop -> clk )) {
308- rc = PTR_ERR (lpc_snoop -> clk );
309- if (rc != - EPROBE_DEFER )
310- dev_err (dev , "couldn't get clock\n" );
311- return rc ;
312- }
313- rc = clk_prepare_enable (lpc_snoop -> clk );
314- if (rc ) {
315- dev_err (dev , "couldn't enable clock\n" );
316- return rc ;
317- }
321+ lpc_snoop -> clk = devm_clk_get_enabled (dev , NULL );
322+ if (IS_ERR (lpc_snoop -> clk ))
323+ return dev_err_probe (dev , PTR_ERR (lpc_snoop -> clk ), "couldn't get clock" );
318324
319325 rc = aspeed_lpc_snoop_config_irq (lpc_snoop , pdev );
320326 if (rc )
321- goto err ;
322-
323- rc = aspeed_lpc_enable_snoop (lpc_snoop , dev , 0 , port );
324- if (rc )
325- goto err ;
326-
327- /* Configuration of 2nd snoop channel port is optional */
328- if (of_property_read_u32_index (dev -> of_node , "snoop-ports" ,
329- 1 , & port ) == 0 ) {
330- rc = aspeed_lpc_enable_snoop (lpc_snoop , dev , 1 , port );
331- if (rc ) {
332- aspeed_lpc_disable_snoop (lpc_snoop , 0 );
333- goto err ;
334- }
335- }
327+ return rc ;
336328
337- return 0 ;
329+ static_assert (ARRAY_SIZE (channel_cfgs ) == ARRAY_SIZE (lpc_snoop -> chan ),
330+ "Broken implementation assumption regarding cfg count" );
331+ for (idx = ASPEED_LPC_SNOOP_INDEX_0 ; idx <= ASPEED_LPC_SNOOP_INDEX_MAX ; idx ++ ) {
332+ u32 port ;
338333
339- err :
340- clk_disable_unprepare (lpc_snoop -> clk );
334+ rc = of_property_read_u32_index (dev -> of_node , "snoop-ports" , idx , & port );
335+ if (rc )
336+ break ;
341337
342- return rc ;
343- }
338+ rc = aspeed_lpc_enable_snoop (dev , lpc_snoop , & lpc_snoop -> chan [idx ],
339+ & channel_cfgs [idx ], port );
340+ if (rc )
341+ goto cleanup_channels ;
342+ }
344343
345- static void aspeed_lpc_snoop_remove (struct platform_device * pdev )
346- {
347- struct aspeed_lpc_snoop * lpc_snoop = dev_get_drvdata (& pdev -> dev );
344+ return idx == ASPEED_LPC_SNOOP_INDEX_0 ? - ENODEV : 0 ;
348345
349- /* Disable both snoop channels */
350- aspeed_lpc_disable_snoop (lpc_snoop , 0 );
351- aspeed_lpc_disable_snoop (lpc_snoop , 1 );
346+ cleanup_channels :
347+ aspeed_lpc_snoop_remove (pdev );
352348
353- clk_disable_unprepare ( lpc_snoop -> clk ) ;
349+ return rc ;
354350}
355351
356352static const struct aspeed_lpc_snoop_model_data ast2400_model_data = {
0 commit comments