1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 Imagination Technologies
4 * Author: Paul Burton <paul.burton@mips.com>
7 #include <linux/kernel.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/property.h>
14 #include <linux/regmap.h>
15 #include <linux/slab.h>
17 #include "line-display.h"
19 struct img_ascii_lcd_ctx
;
22 * struct img_ascii_lcd_config - Configuration information about an LCD model
23 * @num_chars: the number of characters the LCD can display
24 * @external_regmap: true if registers are in a system controller, else false
25 * @ops: character line display operations
27 struct img_ascii_lcd_config
{
28 unsigned int num_chars
;
30 const struct linedisp_ops ops
;
34 * struct img_ascii_lcd_ctx - Private data structure
35 * @linedisp: line display structure
36 * @base: the base address of the LCD registers
37 * @regmap: the regmap through which LCD registers are accessed
38 * @offset: the offset within regmap to the start of the LCD registers
39 * @cfg: pointer to the LCD model configuration
41 struct img_ascii_lcd_ctx
{
42 struct linedisp linedisp
;
45 struct regmap
*regmap
;
48 const struct img_ascii_lcd_config
*cfg
;
52 * MIPS Boston development board
55 static void boston_update(struct linedisp
*linedisp
)
57 struct img_ascii_lcd_ctx
*ctx
=
58 container_of(linedisp
, struct img_ascii_lcd_ctx
, linedisp
);
61 #if BITS_PER_LONG == 64
62 val
= *((u64
*)&linedisp
->buf
[0]);
63 __raw_writeq(val
, ctx
->base
);
64 #elif BITS_PER_LONG == 32
65 val
= *((u32
*)&linedisp
->buf
[0]);
66 __raw_writel(val
, ctx
->base
);
67 val
= *((u32
*)&linedisp
->buf
[4]);
68 __raw_writel(val
, ctx
->base
+ 4);
70 # error Not 32 or 64 bit
74 static struct img_ascii_lcd_config boston_config
= {
77 .update
= boston_update
,
82 * MIPS Malta development board
85 static void malta_update(struct linedisp
*linedisp
)
87 struct img_ascii_lcd_ctx
*ctx
=
88 container_of(linedisp
, struct img_ascii_lcd_ctx
, linedisp
);
92 for (i
= 0; i
< linedisp
->num_chars
; i
++) {
93 err
= regmap_write(ctx
->regmap
,
94 ctx
->offset
+ (i
* 8), linedisp
->buf
[i
]);
100 pr_err_ratelimited("Failed to update LCD display: %d\n", err
);
103 static struct img_ascii_lcd_config malta_config
= {
105 .external_regmap
= true,
107 .update
= malta_update
,
112 * MIPS SEAD3 development board
116 SEAD3_REG_LCD_CTRL
= 0x00,
117 #define SEAD3_REG_LCD_CTRL_SETDRAM BIT(7)
118 SEAD3_REG_LCD_DATA
= 0x08,
119 SEAD3_REG_CPLD_STATUS
= 0x10,
120 #define SEAD3_REG_CPLD_STATUS_BUSY BIT(0)
121 SEAD3_REG_CPLD_DATA
= 0x18,
122 #define SEAD3_REG_CPLD_DATA_BUSY BIT(7)
125 static int sead3_wait_sm_idle(struct img_ascii_lcd_ctx
*ctx
)
131 err
= regmap_read(ctx
->regmap
,
132 ctx
->offset
+ SEAD3_REG_CPLD_STATUS
,
136 } while (status
& SEAD3_REG_CPLD_STATUS_BUSY
);
142 static int sead3_wait_lcd_idle(struct img_ascii_lcd_ctx
*ctx
)
144 unsigned int cpld_data
;
147 err
= sead3_wait_sm_idle(ctx
);
152 err
= regmap_read(ctx
->regmap
,
153 ctx
->offset
+ SEAD3_REG_LCD_CTRL
,
158 err
= sead3_wait_sm_idle(ctx
);
162 err
= regmap_read(ctx
->regmap
,
163 ctx
->offset
+ SEAD3_REG_CPLD_DATA
,
167 } while (cpld_data
& SEAD3_REG_CPLD_DATA_BUSY
);
172 static void sead3_update(struct linedisp
*linedisp
)
174 struct img_ascii_lcd_ctx
*ctx
=
175 container_of(linedisp
, struct img_ascii_lcd_ctx
, linedisp
);
179 for (i
= 0; i
< linedisp
->num_chars
; i
++) {
180 err
= sead3_wait_lcd_idle(ctx
);
184 err
= regmap_write(ctx
->regmap
,
185 ctx
->offset
+ SEAD3_REG_LCD_CTRL
,
186 SEAD3_REG_LCD_CTRL_SETDRAM
| i
);
190 err
= sead3_wait_lcd_idle(ctx
);
194 err
= regmap_write(ctx
->regmap
,
195 ctx
->offset
+ SEAD3_REG_LCD_DATA
,
202 pr_err_ratelimited("Failed to update LCD display: %d\n", err
);
205 static struct img_ascii_lcd_config sead3_config
= {
207 .external_regmap
= true,
209 .update
= sead3_update
,
213 static const struct of_device_id img_ascii_lcd_matches
[] = {
214 { .compatible
= "img,boston-lcd", .data
= &boston_config
},
215 { .compatible
= "mti,malta-lcd", .data
= &malta_config
},
216 { .compatible
= "mti,sead3-lcd", .data
= &sead3_config
},
219 MODULE_DEVICE_TABLE(of
, img_ascii_lcd_matches
);
222 * img_ascii_lcd_probe() - probe an LCD display device
223 * @pdev: the LCD platform device
225 * Probe an LCD display device, ensuring that we have the required resources in
226 * order to access the LCD & setting up private data as well as sysfs files.
228 * Return: 0 on success, else -ERRNO
230 static int img_ascii_lcd_probe(struct platform_device
*pdev
)
232 struct device
*dev
= &pdev
->dev
;
233 const struct img_ascii_lcd_config
*cfg
= device_get_match_data(dev
);
234 struct img_ascii_lcd_ctx
*ctx
;
237 ctx
= devm_kzalloc(dev
, sizeof(*ctx
), GFP_KERNEL
);
241 if (cfg
->external_regmap
) {
242 ctx
->regmap
= syscon_node_to_regmap(dev
->parent
->of_node
);
243 if (IS_ERR(ctx
->regmap
))
244 return PTR_ERR(ctx
->regmap
);
246 if (of_property_read_u32(dev
->of_node
, "offset", &ctx
->offset
))
249 ctx
->base
= devm_platform_ioremap_resource(pdev
, 0);
250 if (IS_ERR(ctx
->base
))
251 return PTR_ERR(ctx
->base
);
254 err
= linedisp_register(&ctx
->linedisp
, dev
, cfg
->num_chars
, &cfg
->ops
);
258 /* for backwards compatibility */
259 err
= compat_only_sysfs_link_entry_to_kobj(&dev
->kobj
,
260 &ctx
->linedisp
.dev
.kobj
,
265 platform_set_drvdata(pdev
, ctx
);
269 linedisp_unregister(&ctx
->linedisp
);
274 * img_ascii_lcd_remove() - remove an LCD display device
275 * @pdev: the LCD platform device
277 * Remove an LCD display device, freeing private resources & ensuring that the
278 * driver stops using the LCD display registers.
280 static void img_ascii_lcd_remove(struct platform_device
*pdev
)
282 struct img_ascii_lcd_ctx
*ctx
= platform_get_drvdata(pdev
);
284 sysfs_remove_link(&pdev
->dev
.kobj
, "message");
285 linedisp_unregister(&ctx
->linedisp
);
288 static struct platform_driver img_ascii_lcd_driver
= {
290 .name
= "img-ascii-lcd",
291 .of_match_table
= img_ascii_lcd_matches
,
293 .probe
= img_ascii_lcd_probe
,
294 .remove_new
= img_ascii_lcd_remove
,
296 module_platform_driver(img_ascii_lcd_driver
);
298 MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
299 MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
300 MODULE_LICENSE("GPL");
301 MODULE_IMPORT_NS(LINEDISP
);