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
40 struct img_ascii_lcd_ctx
{
41 struct linedisp linedisp
;
44 struct regmap
*regmap
;
50 * MIPS Boston development board
53 static void boston_update(struct linedisp
*linedisp
)
55 struct img_ascii_lcd_ctx
*ctx
=
56 container_of(linedisp
, struct img_ascii_lcd_ctx
, linedisp
);
59 #if BITS_PER_LONG == 64
60 val
= *((u64
*)&linedisp
->buf
[0]);
61 __raw_writeq(val
, ctx
->base
);
62 #elif BITS_PER_LONG == 32
63 val
= *((u32
*)&linedisp
->buf
[0]);
64 __raw_writel(val
, ctx
->base
);
65 val
= *((u32
*)&linedisp
->buf
[4]);
66 __raw_writel(val
, ctx
->base
+ 4);
68 # error Not 32 or 64 bit
72 static const struct img_ascii_lcd_config boston_config
= {
75 .update
= boston_update
,
80 * MIPS Malta development board
83 static void malta_update(struct linedisp
*linedisp
)
85 struct img_ascii_lcd_ctx
*ctx
=
86 container_of(linedisp
, struct img_ascii_lcd_ctx
, linedisp
);
90 for (i
= 0; i
< linedisp
->num_chars
; i
++) {
91 err
= regmap_write(ctx
->regmap
,
92 ctx
->offset
+ (i
* 8), linedisp
->buf
[i
]);
98 pr_err_ratelimited("Failed to update LCD display: %d\n", err
);
101 static const struct img_ascii_lcd_config malta_config
= {
103 .external_regmap
= true,
105 .update
= malta_update
,
110 * MIPS SEAD3 development board
114 SEAD3_REG_LCD_CTRL
= 0x00,
115 #define SEAD3_REG_LCD_CTRL_SETDRAM BIT(7)
116 SEAD3_REG_LCD_DATA
= 0x08,
117 SEAD3_REG_CPLD_STATUS
= 0x10,
118 #define SEAD3_REG_CPLD_STATUS_BUSY BIT(0)
119 SEAD3_REG_CPLD_DATA
= 0x18,
120 #define SEAD3_REG_CPLD_DATA_BUSY BIT(7)
123 static int sead3_wait_sm_idle(struct img_ascii_lcd_ctx
*ctx
)
129 err
= regmap_read(ctx
->regmap
,
130 ctx
->offset
+ SEAD3_REG_CPLD_STATUS
,
134 } while (status
& SEAD3_REG_CPLD_STATUS_BUSY
);
140 static int sead3_wait_lcd_idle(struct img_ascii_lcd_ctx
*ctx
)
142 unsigned int cpld_data
;
145 err
= sead3_wait_sm_idle(ctx
);
150 err
= regmap_read(ctx
->regmap
,
151 ctx
->offset
+ SEAD3_REG_LCD_CTRL
,
156 err
= sead3_wait_sm_idle(ctx
);
160 err
= regmap_read(ctx
->regmap
,
161 ctx
->offset
+ SEAD3_REG_CPLD_DATA
,
165 } while (cpld_data
& SEAD3_REG_CPLD_DATA_BUSY
);
170 static void sead3_update(struct linedisp
*linedisp
)
172 struct img_ascii_lcd_ctx
*ctx
=
173 container_of(linedisp
, struct img_ascii_lcd_ctx
, linedisp
);
177 for (i
= 0; i
< linedisp
->num_chars
; i
++) {
178 err
= sead3_wait_lcd_idle(ctx
);
182 err
= regmap_write(ctx
->regmap
,
183 ctx
->offset
+ SEAD3_REG_LCD_CTRL
,
184 SEAD3_REG_LCD_CTRL_SETDRAM
| i
);
188 err
= sead3_wait_lcd_idle(ctx
);
192 err
= regmap_write(ctx
->regmap
,
193 ctx
->offset
+ SEAD3_REG_LCD_DATA
,
200 pr_err_ratelimited("Failed to update LCD display: %d\n", err
);
203 static const struct img_ascii_lcd_config sead3_config
= {
205 .external_regmap
= true,
207 .update
= sead3_update
,
211 static const struct of_device_id img_ascii_lcd_matches
[] = {
212 { .compatible
= "img,boston-lcd", .data
= &boston_config
},
213 { .compatible
= "mti,malta-lcd", .data
= &malta_config
},
214 { .compatible
= "mti,sead3-lcd", .data
= &sead3_config
},
217 MODULE_DEVICE_TABLE(of
, img_ascii_lcd_matches
);
220 * img_ascii_lcd_probe() - probe an LCD display device
221 * @pdev: the LCD platform device
223 * Probe an LCD display device, ensuring that we have the required resources in
224 * order to access the LCD & setting up private data as well as sysfs files.
226 * Return: 0 on success, else -ERRNO
228 static int img_ascii_lcd_probe(struct platform_device
*pdev
)
230 struct device
*dev
= &pdev
->dev
;
231 const struct img_ascii_lcd_config
*cfg
= device_get_match_data(dev
);
232 struct img_ascii_lcd_ctx
*ctx
;
235 ctx
= devm_kzalloc(dev
, sizeof(*ctx
), GFP_KERNEL
);
239 if (cfg
->external_regmap
) {
240 ctx
->regmap
= syscon_node_to_regmap(dev
->parent
->of_node
);
241 if (IS_ERR(ctx
->regmap
))
242 return PTR_ERR(ctx
->regmap
);
244 if (of_property_read_u32(dev
->of_node
, "offset", &ctx
->offset
))
247 ctx
->base
= devm_platform_ioremap_resource(pdev
, 0);
248 if (IS_ERR(ctx
->base
))
249 return PTR_ERR(ctx
->base
);
252 err
= linedisp_register(&ctx
->linedisp
, dev
, cfg
->num_chars
, &cfg
->ops
);
256 /* for backwards compatibility */
257 err
= compat_only_sysfs_link_entry_to_kobj(&dev
->kobj
,
258 &ctx
->linedisp
.dev
.kobj
,
263 platform_set_drvdata(pdev
, ctx
);
267 linedisp_unregister(&ctx
->linedisp
);
272 * img_ascii_lcd_remove() - remove an LCD display device
273 * @pdev: the LCD platform device
275 * Remove an LCD display device, freeing private resources & ensuring that the
276 * driver stops using the LCD display registers.
278 static void img_ascii_lcd_remove(struct platform_device
*pdev
)
280 struct img_ascii_lcd_ctx
*ctx
= platform_get_drvdata(pdev
);
282 sysfs_remove_link(&pdev
->dev
.kobj
, "message");
283 linedisp_unregister(&ctx
->linedisp
);
286 static struct platform_driver img_ascii_lcd_driver
= {
288 .name
= "img-ascii-lcd",
289 .of_match_table
= img_ascii_lcd_matches
,
291 .probe
= img_ascii_lcd_probe
,
292 .remove
= img_ascii_lcd_remove
,
294 module_platform_driver(img_ascii_lcd_driver
);
296 MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
297 MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
298 MODULE_LICENSE("GPL");
299 MODULE_IMPORT_NS("LINEDISP");