Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * RTC driver for the interal RTC block in the Amlogic Meson6, Meson8, |
| 4 | * Meson8b and Meson8m2 SoCs. |
| 5 | * |
| 6 | * The RTC is split in to two parts, the AHB front end and a simple serial |
| 7 | * connection to the actual registers. This driver manages both parts. |
| 8 | * |
| 9 | * Copyright (c) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com> |
| 10 | * Copyright (c) 2015 Ben Dooks <ben.dooks@codethink.co.uk> for Codethink Ltd |
| 11 | * Based on origin by Carlo Caione <carlo@endlessm.com> |
| 12 | */ |
| 13 | |
| 14 | #include <linux/bitfield.h> |
| 15 | #include <linux/delay.h> |
| 16 | #include <linux/io.h> |
| 17 | #include <linux/kernel.h> |
| 18 | #include <linux/module.h> |
| 19 | #include <linux/nvmem-provider.h> |
| 20 | #include <linux/of.h> |
| 21 | #include <linux/platform_device.h> |
| 22 | #include <linux/regmap.h> |
| 23 | #include <linux/regulator/consumer.h> |
| 24 | #include <linux/reset.h> |
| 25 | #include <linux/rtc.h> |
| 26 | |
| 27 | /* registers accessed from cpu bus */ |
| 28 | #define RTC_ADDR0 0x00 |
| 29 | #define RTC_ADDR0_LINE_SCLK BIT(0) |
| 30 | #define RTC_ADDR0_LINE_SEN BIT(1) |
| 31 | #define RTC_ADDR0_LINE_SDI BIT(2) |
| 32 | #define RTC_ADDR0_START_SER BIT(17) |
| 33 | #define RTC_ADDR0_WAIT_SER BIT(22) |
| 34 | #define RTC_ADDR0_DATA GENMASK(31, 24) |
| 35 | |
| 36 | #define RTC_ADDR1 0x04 |
| 37 | #define RTC_ADDR1_SDO BIT(0) |
| 38 | #define RTC_ADDR1_S_READY BIT(1) |
| 39 | |
| 40 | #define RTC_ADDR2 0x08 |
| 41 | #define RTC_ADDR3 0x0c |
| 42 | |
| 43 | #define RTC_REG4 0x10 |
| 44 | #define RTC_REG4_STATIC_VALUE GENMASK(7, 0) |
| 45 | |
| 46 | /* rtc registers accessed via rtc-serial interface */ |
| 47 | #define RTC_COUNTER (0) |
| 48 | #define RTC_SEC_ADJ (2) |
| 49 | #define RTC_REGMEM_0 (4) |
| 50 | #define RTC_REGMEM_1 (5) |
| 51 | #define RTC_REGMEM_2 (6) |
| 52 | #define RTC_REGMEM_3 (7) |
| 53 | |
| 54 | #define RTC_ADDR_BITS (3) /* number of address bits to send */ |
| 55 | #define RTC_DATA_BITS (32) /* number of data bits to tx/rx */ |
| 56 | |
| 57 | #define MESON_STATIC_BIAS_CUR (0x5 << 1) |
| 58 | #define MESON_STATIC_VOLTAGE (0x3 << 11) |
| 59 | #define MESON_STATIC_DEFAULT (MESON_STATIC_BIAS_CUR | MESON_STATIC_VOLTAGE) |
| 60 | |
| 61 | struct meson_rtc { |
| 62 | struct rtc_device *rtc; /* rtc device we created */ |
| 63 | struct device *dev; /* device we bound from */ |
| 64 | struct reset_control *reset; /* reset source */ |
| 65 | struct regulator *vdd; /* voltage input */ |
| 66 | struct regmap *peripheral; /* peripheral registers */ |
| 67 | struct regmap *serial; /* serial registers */ |
| 68 | }; |
| 69 | |
| 70 | static const struct regmap_config meson_rtc_peripheral_regmap_config = { |
| 71 | .name = "peripheral-registers", |
| 72 | .reg_bits = 8, |
| 73 | .val_bits = 32, |
| 74 | .reg_stride = 4, |
| 75 | .max_register = RTC_REG4, |
| 76 | .fast_io = true, |
| 77 | }; |
| 78 | |
| 79 | /* RTC front-end serialiser controls */ |
| 80 | |
| 81 | static void meson_rtc_sclk_pulse(struct meson_rtc *rtc) |
| 82 | { |
| 83 | udelay(5); |
| 84 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, 0); |
| 85 | udelay(5); |
| 86 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, |
| 87 | RTC_ADDR0_LINE_SCLK); |
| 88 | } |
| 89 | |
| 90 | static void meson_rtc_send_bit(struct meson_rtc *rtc, unsigned int bit) |
| 91 | { |
| 92 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, |
| 93 | bit ? RTC_ADDR0_LINE_SDI : 0); |
| 94 | meson_rtc_sclk_pulse(rtc); |
| 95 | } |
| 96 | |
| 97 | static void meson_rtc_send_bits(struct meson_rtc *rtc, u32 data, |
| 98 | unsigned int nr) |
| 99 | { |
| 100 | u32 bit = 1 << (nr - 1); |
| 101 | |
| 102 | while (bit) { |
| 103 | meson_rtc_send_bit(rtc, data & bit); |
| 104 | bit >>= 1; |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | static void meson_rtc_set_dir(struct meson_rtc *rtc, u32 mode) |
| 109 | { |
| 110 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, 0); |
| 111 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0); |
| 112 | meson_rtc_send_bit(rtc, mode); |
| 113 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0); |
| 114 | } |
| 115 | |
| 116 | static u32 meson_rtc_get_data(struct meson_rtc *rtc) |
| 117 | { |
| 118 | u32 tmp, val = 0; |
| 119 | int bit; |
| 120 | |
| 121 | for (bit = 0; bit < RTC_DATA_BITS; bit++) { |
| 122 | meson_rtc_sclk_pulse(rtc); |
| 123 | val <<= 1; |
| 124 | |
| 125 | regmap_read(rtc->peripheral, RTC_ADDR1, &tmp); |
| 126 | val |= tmp & RTC_ADDR1_SDO; |
| 127 | } |
| 128 | |
| 129 | return val; |
| 130 | } |
| 131 | |
| 132 | static int meson_rtc_get_bus(struct meson_rtc *rtc) |
| 133 | { |
Colin Ian King | 60bd22f | 2019-11-22 22:52:10 +0000 | [diff] [blame] | 134 | int ret, retries; |
Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 135 | u32 val; |
| 136 | |
| 137 | /* prepare bus for transfers, set all lines low */ |
| 138 | val = RTC_ADDR0_LINE_SDI | RTC_ADDR0_LINE_SEN | RTC_ADDR0_LINE_SCLK; |
| 139 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, val, 0); |
| 140 | |
| 141 | for (retries = 0; retries < 3; retries++) { |
| 142 | /* wait for the bus to be ready */ |
| 143 | if (!regmap_read_poll_timeout(rtc->peripheral, RTC_ADDR1, val, |
| 144 | val & RTC_ADDR1_S_READY, 10, |
| 145 | 10000)) |
| 146 | return 0; |
| 147 | |
| 148 | dev_warn(rtc->dev, "failed to get bus, resetting RTC\n"); |
| 149 | |
| 150 | ret = reset_control_reset(rtc->reset); |
| 151 | if (ret) |
| 152 | return ret; |
| 153 | } |
| 154 | |
| 155 | dev_err(rtc->dev, "bus is not ready\n"); |
| 156 | return -ETIMEDOUT; |
| 157 | } |
| 158 | |
| 159 | static int meson_rtc_serial_bus_reg_read(void *context, unsigned int reg, |
| 160 | unsigned int *data) |
| 161 | { |
| 162 | struct meson_rtc *rtc = context; |
| 163 | int ret; |
| 164 | |
| 165 | ret = meson_rtc_get_bus(rtc); |
| 166 | if (ret) |
| 167 | return ret; |
| 168 | |
| 169 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, |
| 170 | RTC_ADDR0_LINE_SEN); |
| 171 | meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS); |
| 172 | meson_rtc_set_dir(rtc, 0); |
| 173 | *data = meson_rtc_get_data(rtc); |
| 174 | |
| 175 | return 0; |
| 176 | } |
| 177 | |
| 178 | static int meson_rtc_serial_bus_reg_write(void *context, unsigned int reg, |
| 179 | unsigned int data) |
| 180 | { |
| 181 | struct meson_rtc *rtc = context; |
| 182 | int ret; |
| 183 | |
| 184 | ret = meson_rtc_get_bus(rtc); |
| 185 | if (ret) |
| 186 | return ret; |
| 187 | |
| 188 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, |
| 189 | RTC_ADDR0_LINE_SEN); |
| 190 | meson_rtc_send_bits(rtc, data, RTC_DATA_BITS); |
| 191 | meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS); |
| 192 | meson_rtc_set_dir(rtc, 1); |
| 193 | |
| 194 | return 0; |
| 195 | } |
| 196 | |
| 197 | static const struct regmap_bus meson_rtc_serial_bus = { |
| 198 | .reg_read = meson_rtc_serial_bus_reg_read, |
| 199 | .reg_write = meson_rtc_serial_bus_reg_write, |
| 200 | }; |
| 201 | |
| 202 | static const struct regmap_config meson_rtc_serial_regmap_config = { |
| 203 | .name = "serial-registers", |
| 204 | .reg_bits = 4, |
| 205 | .reg_stride = 1, |
| 206 | .val_bits = 32, |
| 207 | .max_register = RTC_REGMEM_3, |
| 208 | .fast_io = false, |
| 209 | }; |
| 210 | |
| 211 | static int meson_rtc_write_static(struct meson_rtc *rtc, u32 data) |
| 212 | { |
| 213 | u32 tmp; |
| 214 | |
| 215 | regmap_write(rtc->peripheral, RTC_REG4, |
| 216 | FIELD_PREP(RTC_REG4_STATIC_VALUE, (data >> 8))); |
| 217 | |
| 218 | /* write the static value and start the auto serializer */ |
| 219 | tmp = FIELD_PREP(RTC_ADDR0_DATA, (data & 0xff)) | RTC_ADDR0_START_SER; |
| 220 | regmap_update_bits(rtc->peripheral, RTC_ADDR0, |
| 221 | RTC_ADDR0_DATA | RTC_ADDR0_START_SER, tmp); |
| 222 | |
| 223 | /* wait for the auto serializer to complete */ |
| 224 | return regmap_read_poll_timeout(rtc->peripheral, RTC_REG4, tmp, |
| 225 | !(tmp & RTC_ADDR0_WAIT_SER), 10, |
| 226 | 10000); |
| 227 | } |
| 228 | |
| 229 | /* RTC interface layer functions */ |
| 230 | |
| 231 | static int meson_rtc_gettime(struct device *dev, struct rtc_time *tm) |
| 232 | { |
| 233 | struct meson_rtc *rtc = dev_get_drvdata(dev); |
| 234 | u32 time; |
| 235 | int ret; |
| 236 | |
| 237 | ret = regmap_read(rtc->serial, RTC_COUNTER, &time); |
| 238 | if (!ret) |
| 239 | rtc_time64_to_tm(time, tm); |
| 240 | |
| 241 | return ret; |
| 242 | } |
| 243 | |
| 244 | static int meson_rtc_settime(struct device *dev, struct rtc_time *tm) |
| 245 | { |
| 246 | struct meson_rtc *rtc = dev_get_drvdata(dev); |
| 247 | |
| 248 | return regmap_write(rtc->serial, RTC_COUNTER, rtc_tm_to_time64(tm)); |
| 249 | } |
| 250 | |
| 251 | static const struct rtc_class_ops meson_rtc_ops = { |
| 252 | .read_time = meson_rtc_gettime, |
| 253 | .set_time = meson_rtc_settime, |
| 254 | }; |
| 255 | |
| 256 | /* NVMEM interface layer functions */ |
| 257 | |
| 258 | static int meson_rtc_regmem_read(void *context, unsigned int offset, |
| 259 | void *buf, size_t bytes) |
| 260 | { |
| 261 | struct meson_rtc *rtc = context; |
| 262 | unsigned int read_offset, read_size; |
| 263 | |
| 264 | read_offset = RTC_REGMEM_0 + (offset / 4); |
| 265 | read_size = bytes / 4; |
| 266 | |
| 267 | return regmap_bulk_read(rtc->serial, read_offset, buf, read_size); |
| 268 | } |
| 269 | |
| 270 | static int meson_rtc_regmem_write(void *context, unsigned int offset, |
| 271 | void *buf, size_t bytes) |
| 272 | { |
| 273 | struct meson_rtc *rtc = context; |
| 274 | unsigned int write_offset, write_size; |
| 275 | |
| 276 | write_offset = RTC_REGMEM_0 + (offset / 4); |
| 277 | write_size = bytes / 4; |
| 278 | |
| 279 | return regmap_bulk_write(rtc->serial, write_offset, buf, write_size); |
| 280 | } |
| 281 | |
| 282 | static int meson_rtc_probe(struct platform_device *pdev) |
| 283 | { |
| 284 | struct nvmem_config meson_rtc_nvmem_config = { |
| 285 | .name = "meson-rtc-regmem", |
| 286 | .type = NVMEM_TYPE_BATTERY_BACKED, |
| 287 | .word_size = 4, |
| 288 | .stride = 4, |
| 289 | .size = 4 * 4, |
| 290 | .reg_read = meson_rtc_regmem_read, |
| 291 | .reg_write = meson_rtc_regmem_write, |
| 292 | }; |
| 293 | struct device *dev = &pdev->dev; |
| 294 | struct meson_rtc *rtc; |
Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 295 | void __iomem *base; |
| 296 | int ret; |
| 297 | u32 tm; |
| 298 | |
| 299 | rtc = devm_kzalloc(dev, sizeof(struct meson_rtc), GFP_KERNEL); |
| 300 | if (!rtc) |
| 301 | return -ENOMEM; |
| 302 | |
| 303 | rtc->rtc = devm_rtc_allocate_device(dev); |
| 304 | if (IS_ERR(rtc->rtc)) |
| 305 | return PTR_ERR(rtc->rtc); |
| 306 | |
| 307 | platform_set_drvdata(pdev, rtc); |
| 308 | |
| 309 | rtc->dev = dev; |
| 310 | |
| 311 | rtc->rtc->ops = &meson_rtc_ops; |
| 312 | rtc->rtc->range_max = U32_MAX; |
| 313 | |
YueHaibing | 09ef18b | 2019-10-06 18:29:20 +0800 | [diff] [blame] | 314 | base = devm_platform_ioremap_resource(pdev, 0); |
Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 315 | if (IS_ERR(base)) |
| 316 | return PTR_ERR(base); |
| 317 | |
| 318 | rtc->peripheral = devm_regmap_init_mmio(dev, base, |
| 319 | &meson_rtc_peripheral_regmap_config); |
| 320 | if (IS_ERR(rtc->peripheral)) { |
| 321 | dev_err(dev, "failed to create peripheral regmap\n"); |
| 322 | return PTR_ERR(rtc->peripheral); |
| 323 | } |
| 324 | |
| 325 | rtc->reset = devm_reset_control_get(dev, NULL); |
| 326 | if (IS_ERR(rtc->reset)) { |
| 327 | dev_err(dev, "missing reset line\n"); |
| 328 | return PTR_ERR(rtc->reset); |
| 329 | } |
| 330 | |
| 331 | rtc->vdd = devm_regulator_get(dev, "vdd"); |
| 332 | if (IS_ERR(rtc->vdd)) { |
| 333 | dev_err(dev, "failed to get the vdd-supply\n"); |
| 334 | return PTR_ERR(rtc->vdd); |
| 335 | } |
| 336 | |
| 337 | ret = regulator_enable(rtc->vdd); |
| 338 | if (ret) { |
| 339 | dev_err(dev, "failed to enable vdd-supply\n"); |
| 340 | return ret; |
| 341 | } |
| 342 | |
| 343 | ret = meson_rtc_write_static(rtc, MESON_STATIC_DEFAULT); |
| 344 | if (ret) { |
| 345 | dev_err(dev, "failed to set static values\n"); |
| 346 | goto out_disable_vdd; |
| 347 | } |
| 348 | |
| 349 | rtc->serial = devm_regmap_init(dev, &meson_rtc_serial_bus, rtc, |
| 350 | &meson_rtc_serial_regmap_config); |
| 351 | if (IS_ERR(rtc->serial)) { |
| 352 | dev_err(dev, "failed to create serial regmap\n"); |
| 353 | ret = PTR_ERR(rtc->serial); |
| 354 | goto out_disable_vdd; |
| 355 | } |
| 356 | |
| 357 | /* |
| 358 | * check if we can read RTC counter, if not then the RTC is probably |
| 359 | * not functional. If it isn't probably best to not bind. |
| 360 | */ |
| 361 | ret = regmap_read(rtc->serial, RTC_COUNTER, &tm); |
| 362 | if (ret) { |
| 363 | dev_err(dev, "cannot read RTC counter, RTC not functional\n"); |
| 364 | goto out_disable_vdd; |
| 365 | } |
| 366 | |
| 367 | meson_rtc_nvmem_config.priv = rtc; |
Bartosz Golaszewski | 3a905c2d | 2020-11-09 17:34:06 +0100 | [diff] [blame] | 368 | ret = devm_rtc_nvmem_register(rtc->rtc, &meson_rtc_nvmem_config); |
Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 369 | if (ret) |
| 370 | goto out_disable_vdd; |
| 371 | |
Bartosz Golaszewski | fdcfd85 | 2020-11-09 17:34:08 +0100 | [diff] [blame] | 372 | ret = devm_rtc_register_device(rtc->rtc); |
Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 373 | if (ret) |
Alexandre Belloni | 5736610 | 2019-02-11 10:46:46 +0100 | [diff] [blame] | 374 | goto out_disable_vdd; |
Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 375 | |
| 376 | return 0; |
| 377 | |
Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 378 | out_disable_vdd: |
| 379 | regulator_disable(rtc->vdd); |
| 380 | return ret; |
| 381 | } |
| 382 | |
Alexandre Belloni | ef886c44 | 2021-02-02 12:22:07 +0100 | [diff] [blame] | 383 | static const __maybe_unused struct of_device_id meson_rtc_dt_match[] = { |
Martin Blumenstingl | d8fe600 | 2019-02-09 01:18:13 +0100 | [diff] [blame] | 384 | { .compatible = "amlogic,meson6-rtc", }, |
| 385 | { .compatible = "amlogic,meson8-rtc", }, |
| 386 | { .compatible = "amlogic,meson8b-rtc", }, |
| 387 | { .compatible = "amlogic,meson8m2-rtc", }, |
| 388 | { }, |
| 389 | }; |
| 390 | MODULE_DEVICE_TABLE(of, meson_rtc_dt_match); |
| 391 | |
| 392 | static struct platform_driver meson_rtc_driver = { |
| 393 | .probe = meson_rtc_probe, |
| 394 | .driver = { |
| 395 | .name = "meson-rtc", |
| 396 | .of_match_table = of_match_ptr(meson_rtc_dt_match), |
| 397 | }, |
| 398 | }; |
| 399 | module_platform_driver(meson_rtc_driver); |
| 400 | |
| 401 | MODULE_DESCRIPTION("Amlogic Meson RTC Driver"); |
| 402 | MODULE_AUTHOR("Ben Dooks <ben.doosk@codethink.co.uk>"); |
| 403 | MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); |
| 404 | MODULE_LICENSE("GPL v2"); |
| 405 | MODULE_ALIAS("platform:meson-rtc"); |