0001
0002
0003
0004
0005
0006
0007 #include <linux/kernel.h>
0008 #include <linux/io.h>
0009 #include <linux/mfd/syscon.h>
0010 #include <linux/module.h>
0011 #include <linux/of_address.h>
0012 #include <linux/of_platform.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/regmap.h>
0015 #include <linux/slab.h>
0016
0017 #include "line-display.h"
0018
0019 struct img_ascii_lcd_ctx;
0020
0021
0022
0023
0024
0025
0026
0027 struct img_ascii_lcd_config {
0028 unsigned int num_chars;
0029 bool external_regmap;
0030 void (*update)(struct linedisp *linedisp);
0031 };
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042 struct img_ascii_lcd_ctx {
0043 union {
0044 void __iomem *base;
0045 struct regmap *regmap;
0046 };
0047 u32 offset;
0048 const struct img_ascii_lcd_config *cfg;
0049 struct linedisp linedisp;
0050 char curr[] __aligned(8);
0051 };
0052
0053
0054
0055
0056
0057 static void boston_update(struct linedisp *linedisp)
0058 {
0059 struct img_ascii_lcd_ctx *ctx =
0060 container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
0061 ulong val;
0062
0063 #if BITS_PER_LONG == 64
0064 val = *((u64 *)&ctx->curr[0]);
0065 __raw_writeq(val, ctx->base);
0066 #elif BITS_PER_LONG == 32
0067 val = *((u32 *)&ctx->curr[0]);
0068 __raw_writel(val, ctx->base);
0069 val = *((u32 *)&ctx->curr[4]);
0070 __raw_writel(val, ctx->base + 4);
0071 #else
0072 # error Not 32 or 64 bit
0073 #endif
0074 }
0075
0076 static struct img_ascii_lcd_config boston_config = {
0077 .num_chars = 8,
0078 .update = boston_update,
0079 };
0080
0081
0082
0083
0084
0085 static void malta_update(struct linedisp *linedisp)
0086 {
0087 struct img_ascii_lcd_ctx *ctx =
0088 container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
0089 unsigned int i;
0090 int err = 0;
0091
0092 for (i = 0; i < linedisp->num_chars; i++) {
0093 err = regmap_write(ctx->regmap,
0094 ctx->offset + (i * 8), ctx->curr[i]);
0095 if (err)
0096 break;
0097 }
0098
0099 if (unlikely(err))
0100 pr_err_ratelimited("Failed to update LCD display: %d\n", err);
0101 }
0102
0103 static struct img_ascii_lcd_config malta_config = {
0104 .num_chars = 8,
0105 .external_regmap = true,
0106 .update = malta_update,
0107 };
0108
0109
0110
0111
0112
0113 enum {
0114 SEAD3_REG_LCD_CTRL = 0x00,
0115 #define SEAD3_REG_LCD_CTRL_SETDRAM BIT(7)
0116 SEAD3_REG_LCD_DATA = 0x08,
0117 SEAD3_REG_CPLD_STATUS = 0x10,
0118 #define SEAD3_REG_CPLD_STATUS_BUSY BIT(0)
0119 SEAD3_REG_CPLD_DATA = 0x18,
0120 #define SEAD3_REG_CPLD_DATA_BUSY BIT(7)
0121 };
0122
0123 static int sead3_wait_sm_idle(struct img_ascii_lcd_ctx *ctx)
0124 {
0125 unsigned int status;
0126 int err;
0127
0128 do {
0129 err = regmap_read(ctx->regmap,
0130 ctx->offset + SEAD3_REG_CPLD_STATUS,
0131 &status);
0132 if (err)
0133 return err;
0134 } while (status & SEAD3_REG_CPLD_STATUS_BUSY);
0135
0136 return 0;
0137
0138 }
0139
0140 static int sead3_wait_lcd_idle(struct img_ascii_lcd_ctx *ctx)
0141 {
0142 unsigned int cpld_data;
0143 int err;
0144
0145 err = sead3_wait_sm_idle(ctx);
0146 if (err)
0147 return err;
0148
0149 do {
0150 err = regmap_read(ctx->regmap,
0151 ctx->offset + SEAD3_REG_LCD_CTRL,
0152 &cpld_data);
0153 if (err)
0154 return err;
0155
0156 err = sead3_wait_sm_idle(ctx);
0157 if (err)
0158 return err;
0159
0160 err = regmap_read(ctx->regmap,
0161 ctx->offset + SEAD3_REG_CPLD_DATA,
0162 &cpld_data);
0163 if (err)
0164 return err;
0165 } while (cpld_data & SEAD3_REG_CPLD_DATA_BUSY);
0166
0167 return 0;
0168 }
0169
0170 static void sead3_update(struct linedisp *linedisp)
0171 {
0172 struct img_ascii_lcd_ctx *ctx =
0173 container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
0174 unsigned int i;
0175 int err = 0;
0176
0177 for (i = 0; i < linedisp->num_chars; i++) {
0178 err = sead3_wait_lcd_idle(ctx);
0179 if (err)
0180 break;
0181
0182 err = regmap_write(ctx->regmap,
0183 ctx->offset + SEAD3_REG_LCD_CTRL,
0184 SEAD3_REG_LCD_CTRL_SETDRAM | i);
0185 if (err)
0186 break;
0187
0188 err = sead3_wait_lcd_idle(ctx);
0189 if (err)
0190 break;
0191
0192 err = regmap_write(ctx->regmap,
0193 ctx->offset + SEAD3_REG_LCD_DATA,
0194 ctx->curr[i]);
0195 if (err)
0196 break;
0197 }
0198
0199 if (unlikely(err))
0200 pr_err_ratelimited("Failed to update LCD display: %d\n", err);
0201 }
0202
0203 static struct img_ascii_lcd_config sead3_config = {
0204 .num_chars = 16,
0205 .external_regmap = true,
0206 .update = sead3_update,
0207 };
0208
0209 static const struct of_device_id img_ascii_lcd_matches[] = {
0210 { .compatible = "img,boston-lcd", .data = &boston_config },
0211 { .compatible = "mti,malta-lcd", .data = &malta_config },
0212 { .compatible = "mti,sead3-lcd", .data = &sead3_config },
0213 { }
0214 };
0215 MODULE_DEVICE_TABLE(of, img_ascii_lcd_matches);
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 static int img_ascii_lcd_probe(struct platform_device *pdev)
0227 {
0228 const struct of_device_id *match;
0229 const struct img_ascii_lcd_config *cfg;
0230 struct device *dev = &pdev->dev;
0231 struct img_ascii_lcd_ctx *ctx;
0232 int err;
0233
0234 match = of_match_device(img_ascii_lcd_matches, dev);
0235 if (!match)
0236 return -ENODEV;
0237
0238 cfg = match->data;
0239 ctx = devm_kzalloc(dev, sizeof(*ctx) + cfg->num_chars, GFP_KERNEL);
0240 if (!ctx)
0241 return -ENOMEM;
0242
0243 if (cfg->external_regmap) {
0244 ctx->regmap = syscon_node_to_regmap(dev->parent->of_node);
0245 if (IS_ERR(ctx->regmap))
0246 return PTR_ERR(ctx->regmap);
0247
0248 if (of_property_read_u32(dev->of_node, "offset", &ctx->offset))
0249 return -EINVAL;
0250 } else {
0251 ctx->base = devm_platform_ioremap_resource(pdev, 0);
0252 if (IS_ERR(ctx->base))
0253 return PTR_ERR(ctx->base);
0254 }
0255
0256 err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, ctx->curr,
0257 cfg->update);
0258 if (err)
0259 return err;
0260
0261
0262 err = compat_only_sysfs_link_entry_to_kobj(&dev->kobj,
0263 &ctx->linedisp.dev.kobj,
0264 "message", NULL);
0265 if (err)
0266 goto err_unregister;
0267
0268 platform_set_drvdata(pdev, ctx);
0269 return 0;
0270
0271 err_unregister:
0272 linedisp_unregister(&ctx->linedisp);
0273 return err;
0274 }
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284
0285 static int img_ascii_lcd_remove(struct platform_device *pdev)
0286 {
0287 struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev);
0288
0289 sysfs_remove_link(&pdev->dev.kobj, "message");
0290 linedisp_unregister(&ctx->linedisp);
0291 return 0;
0292 }
0293
0294 static struct platform_driver img_ascii_lcd_driver = {
0295 .driver = {
0296 .name = "img-ascii-lcd",
0297 .of_match_table = img_ascii_lcd_matches,
0298 },
0299 .probe = img_ascii_lcd_probe,
0300 .remove = img_ascii_lcd_remove,
0301 };
0302 module_platform_driver(img_ascii_lcd_driver);
0303
0304 MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
0305 MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
0306 MODULE_LICENSE("GPL");