Airport codes in the US and Canada were originally based on the two-letter weather station codes for nearby cities. For instance, Toronto (Pemberton) was "TO".
However, it was soon apparent that didn't provide enough codes, so it was expanded to 3 letters. Canada chose to prefix all their codes with "Y" so they would be easily recognised, e.g. "YTO" for Toronto, whereas the US added a suffix letter, such as "LAX" for Los Angeles. These are the IATA codes you know today.
Then ICAO wanted even more codes to cover the entire world, but assigned on a more geographical basis than the seemingly random IATA codes. Canada was prefixed again, this time as region "C", e.g. "CYTO", and the contiguous US was prefixed as region "K", e.g. "KLAX".
When Toronto Pearson airport was built, it needed a new code because YTO/CYTO was already taken; I can only guess that they chose YYZ/CYYZ because there was no weather station using code YZ. Canada later added Z/CZ prefixes, and now allows any letters just like the US.
Note that the US and Canada got single-letter ICAO prefixes due to their large land area. Most countries are smaller and thus got two-letter prefixes, with the first letter specifying a region, e.g. "E" for Northern Europe, and the second specifying a country, e.g. "G" for Great Britain. This leaves no obvious mapping to/from the IATA code, e.g. EGLL vs LHR.